Skip to main content

macroonz_compiler/token/capture/
types.rs

1//! The capture home's declarations: what one captured declaration is, how a producer's span table answers, and how a text read refuses.
2//!
3//! Declarations only.
4//! Every road that reaches a private field lives in `type_guard.rs`, this file's own child, which is where all five magnitudes below are settled.
5
6use crate::bounded::Bounded;
7
8#[path = "type_guard.rs"]
9mod guard;
10
11/// Steps one token path may carry, and so how deeply a declared input may nest.
12///
13/// A width bound alone bounds each level and says nothing about the depth, so an input nested a million groups deep would satisfy it at every level while the walk reading it did not terminate.
14pub const TOKEN_PATH_DEPTH_LIMIT: usize = 32;
15
16/// Token trees one captured input may carry at any one nesting level.
17pub const CAPTURED_TOKEN_LIMIT: usize = 4096;
18
19/// Tokens one captured input may carry across the whole tree, and positions one span table may hold.
20///
21/// The level bound and the depth bound multiply, so the total is bounded in its own right rather than left as the product of two other magnitudes; a table is not a level, so it stands here too.
22pub const CAPTURED_TREE_TOKEN_LIMIT: usize = 16_384;
23
24/// Units of capture work one walk may spend, one unit per examined token.
25///
26/// Deliberately wider than the whole-tree magnitude, because a walk may look at more than it keeps, and a budget at the tree magnitude exactly would refuse a lawful input the moment its producer looked twice at anything.
27pub const CAPTURE_WORK_LIMIT: usize = 65_536;
28
29/// Source bytes one text capture may read before tokenization.
30///
31/// This magnitude is independent of token count, tree depth, and capture work so a hostile trivia-only input cannot evade every structural bound by producing no retained token.
32pub const TEXT_SOURCE_BYTE_LIMIT: usize = 65_536;
33
34/// An opaque index into the producer's span table.
35///
36/// It carries no position, no file, and no length: the producer built the table while capturing, and only the producer can turn one back into a compiler span.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub struct SpanHandle(u32);
39
40/// The coordinate system one source position is counted in.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub enum CoordinateRole {
43    /// A zero-based byte offset in the captured text.
44    Byte,
45    /// A zero-based ordinal retained by the source producer.
46    SemanticOrigin,
47}
48
49/// One compiler-local source position with its coordinate system stated.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub struct SourceCoordinate {
52    /// The coordinate system in which the position is counted.
53    pub role: CoordinateRole,
54    /// The zero-based position in that coordinate system.
55    pub position: u64,
56}
57
58/// The delimiter one captured group is written with.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub enum CapturedDelimiter {
61    /// `( … )`.
62    Parenthesis,
63    /// `{ … }`.
64    Brace,
65    /// `[ … ]`.
66    Bracket,
67    /// A group with no delimiter written — the invisible grouping a compiler inserts around a captured fragment.
68    ///
69    /// It is a real group and is never flattened away, and a reader of text can never write one, because text that carries no delimiter carries no group.
70    Bare,
71}
72
73/// Which declared magnitude one capture ran past.
74///
75/// Every row refuses before any partial tree exists: a truncated capture is a different declaration, and capturing one would put everything downstream to work on material nobody wrote.
76#[must_use = "a bound refusal names which declared magnitude the capture would have passed"]
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78pub enum CaptureBound {
79    /// The declared input nests deeper than the declared magnitude.
80    Depth,
81    /// One nesting level carries more token trees than the declared magnitude.
82    Level,
83    /// The whole tree carries more tokens than the declared magnitude.
84    Tree,
85    /// The walk spent the declared capture-work budget.
86    Work,
87}
88
89/// Where one captured token sits, as the index route from the root of the declared input.
90///
91/// The route is unique by construction: `[3, 0, 5]` is the sixth token of the first token of the fourth top-level token, and nothing else in the tree spells that.
92/// It is stable under everything a span is not stable under — which producer read the input, where the file moved, how the source was formatted — so two captures of one declaration agree on every route.
93#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct TokenPath {
95    steps: Bounded<u32, TOKEN_PATH_DEPTH_LIMIT>,
96}
97
98/// The running state of one capture walk: what the walk has spent, and how much of the whole-tree magnitude it has taken.
99///
100/// The two are charged separately, because a producer that reads material it discards — a frontend skipping trivia, a reader backtracking over an alternative — spends work the result never shows, and the budget is the only magnitude that can see it.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
102pub struct CaptureWalk {
103    remaining: usize,
104    taken: usize,
105}
106
107/// One non-group value a capture producer offers to the checked builder.
108///
109/// Groups have their own builder operation so no caller can smuggle child trees carrying foreign paths or handles through an atom seat.
110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
111pub enum CapturedAtom {
112    /// An ordinary identifier-shaped word or keyword.
113    Word(String),
114    /// One punctuation character that stands alone.
115    Punct(char),
116    /// A text literal's text.
117    Text(String),
118    /// A numeric literal, exactly as written.
119    Number(String),
120    /// A byte-string literal's material.
121    ByteText(Vec<u8>),
122    /// One character literal's character.
123    Character(char),
124    /// One byte literal's byte.
125    Byte(u8),
126    /// A C string literal's material without its terminating NUL.
127    NulTerminatedText(Vec<u8>),
128    /// A raw identifier's name without its `r#` spelling marker.
129    RawIdentifier(String),
130    /// One punctuation character joined to the token after it.
131    JointPunct(char),
132}
133
134/// What one captured token carries.
135///
136/// An arm carries a literal's value and never the characters it was spelled with, so `"x"` and `r"x"` are one text and which prefix a producer read is not a fact the tree keeps.
137///
138/// # Ordering
139///
140/// The roster grows at its end and nowhere else: each arm's slot is a byte of the canonical bytes a captured declaration's identity is derived over.
141#[derive(Debug, Clone, PartialEq, Eq, Hash)]
142pub enum CapturedPayload {
143    /// An ordinary identifier-shaped word or keyword.
144    Word(String),
145    /// One punctuation character that stands alone.
146    Punct(char),
147    /// A text literal's text: `"…"` and `r"…"` alike, escapes read and quotes removed.
148    Text(String),
149    /// A numeric literal, exactly as written: the base, the digit separators, and the suffix that types it are all part of what the declaration says.
150    Number(String),
151    /// A delimited group and the tokens inside it.
152    Group {
153        /// The delimiter written around the group.
154        delimiter: CapturedDelimiter,
155        /// The tokens inside, in the order they were written.
156        trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
157    },
158    /// A byte-string literal's material: `b"…"` and `br"…"`, kept as bytes because material that is not text crosses without a lossy road existing for it to take.
159    ByteText(Vec<u8>),
160    /// One character literal's character: `'…'`.
161    Character(char),
162    /// One byte literal's byte: `b'…'`.
163    Byte(u8),
164    /// A C string literal's material: `c"…"` and `cr"…"`, without the terminating NUL, which is the literal form's and never the value's.
165    NulTerminatedText(Vec<u8>),
166    /// A raw identifier's name without its `r#` spelling marker.
167    RawIdentifier(String),
168    /// One punctuation character joined to the token after it.
169    JointPunct(char),
170}
171
172/// Why one literal spelling could not be read into the value it names.
173///
174/// Neither row is the caller's mistake: every spelling that reaches this road was already lexed by a compiler, so a refusal is this crate saying it does not read what the compiler admitted.
175#[must_use = "a literal refusal names why the spelling could not be read into a value"]
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
177pub enum LiteralReadCause {
178    /// The spelling opens with no literal form this grammar has a row for.
179    NotAKnownForm,
180    /// The form is one this grammar reads, and its body carries material this grammar could not read the value of.
181    NotReadable,
182}
183
184/// One captured token: what it carries, where it sits, and how to reach the compiler span it came from.
185#[derive(Debug, Clone, PartialEq, Eq, Hash)]
186pub struct CapturedTokenTree {
187    payload: CapturedPayload,
188    path: TokenPath,
189    span: SpanHandle,
190}
191
192/// One captured declared input: the top-level token trees, and how many span handles the producer issued.
193#[derive(Debug, Clone, PartialEq, Eq, Hash)]
194pub struct CapturedInput {
195    trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
196    issued: usize,
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
200enum CaptureBuilderStanding {
201    Ready,
202    Refused { retained_before_capture: usize },
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
206enum CaptureLevelStanding {
207    Open,
208    Finished,
209}
210
211/// The only state that issues capture handles and retains the producer's matching source positions.
212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
213pub struct CaptureBuilder<Position> {
214    positions: Vec<Position>,
215    walk: CaptureWalk,
216    standing: CaptureBuilderStanding,
217}
218
219/// One nesting level borrowed from a [`CaptureBuilder`].
220///
221/// A producer can append atoms or groups and cannot state a path, a handle, or a denominator.
222/// Every operation consumes the level, and only a successful operation returns it, so a refused partial level cannot be finished.
223pub struct CaptureLevel<'capture, Position> {
224    positions: &'capture mut Vec<Position>,
225    walk: &'capture mut CaptureWalk,
226    builder_standing: &'capture mut CaptureBuilderStanding,
227    retained_before_capture: usize,
228    path: TokenPath,
229    trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
230    standing: CaptureLevelStanding,
231}
232
233/// Why a checked capture was not completed.
234#[must_use = "a capture refusal names whether a declared bound or the producer's own reading stopped construction"]
235#[derive(Debug, Clone, PartialEq, Eq, Hash)]
236pub enum CaptureBuildRefusal<Position, ProducerRefusal> {
237    /// One declared capture magnitude was exceeded at this producer position.
238    Unbounded {
239        /// The magnitude exceeded.
240        bound: CaptureBound,
241        /// The producer's own position for the token that reached it.
242        at: Position,
243    },
244    /// The producer could not read one token after the builder issued its declaration path and producer handle.
245    ProducerRefused {
246        /// The producer's typed reason.
247        cause: ProducerRefusal,
248        /// The declaration-local route to the token the producer could not read.
249        path: TokenPath,
250        /// The handle already bound to the token's retained source position.
251        at: SpanHandle,
252    },
253}
254
255/// Why one span table could not say where a handle sits.
256///
257/// A caller holding the handle and the table's reach can tell a handle issued by another producer from a handle issued past the end of a truncated table, which is the whole of what is knowable from this side.
258#[must_use = "a resolution refusal carries the handle and how far the table reaches"]
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
260pub struct SpanResolutionRefusal {
261    /// The handle the table was asked to resolve.
262    pub handle: SpanHandle,
263    /// How many positions the table carries; a handle at or past this index names no position in it.
264    pub reaches: usize,
265}
266
267/// How a producer answers "where is the token this handle names?".
268///
269/// Not an option and not a default: nothing here invents a position for a handle it cannot resolve, and a diagnostic coordinate reading `byte 0` under a producer-held table would be a fiction.
270#[derive(Debug, Clone, PartialEq, Eq, Hash)]
271pub enum SpanTable {
272    /// Byte offsets into the declared input, one per issued handle.
273    ByteOffsets(Bounded<u64, CAPTURED_TREE_TOKEN_LIMIT>),
274    /// The producer holds the compiler's spans and resolves handles itself.
275    ProducerHeld,
276}
277
278/// Why the low-level lexer could not normalize one spelling.
279#[must_use = "a lexical refusal names the spelling distinction that could not be normalized"]
280#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
281pub enum TextLexicalCause {
282    /// A block comment was not terminated.
283    BlockCommentNotTerminated,
284    /// An identifier contains a character the compiler lexer rejects.
285    InvalidIdentifier,
286    /// A prefix is reserved or not meaningful without an edition-aware parser.
287    UnknownPrefix,
288    /// A lifetime prefix is reserved or not meaningful without an edition-aware parser.
289    UnknownLifetimePrefix,
290    /// A guarded-string prefix requires parser context this boundary does not own.
291    GuardedStringPrefix,
292    /// A literal carries a malformed low-level spelling.
293    MalformedLiteral,
294    /// A lifetime begins with a number.
295    LifetimeStartsWithNumber,
296    /// Frontmatter is not Rust token input at this boundary.
297    Frontmatter,
298    /// The lexer reported a character with no lawful Rust token kind.
299    UnknownToken,
300}
301
302/// Why one text read refused.
303///
304/// Dependent checks: there is no group to balance until the characters were cut, and no magnitude to exceed until the trees were built.
305#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
306pub enum TextReadCause {
307    /// A literal was never closed.
308    NotTerminated,
309    /// A literal carries an escape sequence the literal owner could not read.
310    NotEscapeFree,
311    /// A delimited group was never closed.
312    NotBalanced,
313    /// A closing delimiter arrived with no group open.
314    NotOpened,
315    /// The declared text exceeds the independent source-byte magnitude.
316    SourceBytesUnbounded,
317    /// The low-level lexer established a malformed or context-dependent spelling.
318    Lexical(TextLexicalCause),
319    /// The read exceeds a declared magnitude, and this is which one — a reader told only "unbounded" cannot tell a tree that nests too deep from one that spends the walk's budget.
320    Unbounded(CaptureBound),
321}
322
323/// One refused text read: the established cause, and the byte it sits at.
324#[must_use = "a read refusal carries the established cause and the byte it sits at"]
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
326pub struct TextReadRefusal {
327    /// The established cause.
328    pub cause: TextReadCause,
329    /// The byte position the cause was established at.
330    pub at: u64,
331}
332
333/// One declared input read from text: the captured trees, and the byte offsets that resolve every handle the read issued.
334///
335/// The callable route — a compiler is one producer of captured input, a test is another, and text is the third — so that the reproduction route a diagnostic names is a real road and not a promise.
336/// The two seats are visible to `text.rs` alone, and that read establishes the relationship between them: the offsets table resolves exactly the handles the capture beside it issued.
337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
338pub struct TextCapture {
339    /// The captured input the read produced.
340    pub(super) input: CapturedInput,
341    /// The byte offsets that resolve the handles that read issued.
342    pub(super) spans: SpanTable,
343}