Skip to main content

macroonz_compiler/token/capture/
types.rs

1//! The capture home's declarations: what one captured declaration is and how a producer's span table answers.
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 four 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 retain across the whole tree, and positions one span table may hold.
20///
21/// The capture-work budget owns the denominator.
22/// One complete nesting level remains available for examined material that does not become a retained token.
23pub const CAPTURED_TREE_TOKEN_LIMIT: usize = CAPTURE_WORK_LIMIT - CAPTURED_TOKEN_LIMIT;
24
25/// Units of capture work one walk may spend, one unit per examined token.
26///
27/// 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.
28pub const CAPTURE_WORK_LIMIT: usize = 65_536;
29
30/// An opaque index into the producer's span table.
31///
32/// 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.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct SpanHandle(u32);
35
36/// The coordinate system one source position is counted in.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum CoordinateRole {
39    /// A zero-based byte offset in the captured text.
40    Byte,
41    /// A zero-based ordinal retained by the source producer.
42    SemanticOrigin,
43}
44
45/// One compiler-local source position with its coordinate system stated.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47pub struct SourceCoordinate {
48    /// The coordinate system in which the position is counted.
49    pub role: CoordinateRole,
50    /// The zero-based position in that coordinate system.
51    pub position: u64,
52}
53
54/// The delimiter one captured group is written with.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub enum CapturedDelimiter {
57    /// `( … )`.
58    Parenthesis,
59    /// `{ … }`.
60    Brace,
61    /// `[ … ]`.
62    Bracket,
63    /// A group with no delimiter written — the invisible grouping a compiler inserts around a captured fragment.
64    ///
65    /// 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.
66    Bare,
67}
68
69crate::roster! {
70    /// Which declared magnitude one capture ran past.
71    ///
72    /// 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.
73    #[must_use = "a bound refusal names which declared magnitude the capture would have passed"]
74    pub enum CaptureBound {
75        /// The declared input nests deeper than the declared magnitude.
76        Depth = "depth",
77        /// One nesting level carries more token trees than the declared magnitude.
78        Level = "level",
79        /// The whole tree carries more tokens than the declared magnitude.
80        Tree = "tree",
81        /// The walk spent the declared capture-work budget.
82        Work = "work",
83    }
84}
85
86/// Where one captured token sits, as the index route from the root of the declared input.
87///
88/// 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.
89/// 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.
90#[derive(Debug, Clone, PartialEq, Eq, Hash)]
91pub struct TokenPath {
92    steps: Bounded<u32, TOKEN_PATH_DEPTH_LIMIT>,
93}
94
95/// The running state of one capture walk: what the walk has spent, and how much of the whole-tree magnitude it has taken.
96///
97/// 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.
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99pub struct CaptureWalk {
100    remaining: usize,
101    taken: usize,
102}
103
104/// One non-group value a capture producer offers to the checked builder.
105///
106/// Groups have their own builder operation so no caller can smuggle child trees carrying foreign paths or handles through an atom seat.
107#[derive(Debug, Clone, PartialEq, Eq, Hash)]
108pub enum CapturedAtom {
109    /// An ordinary identifier-shaped word or keyword.
110    Word(String),
111    /// One punctuation character that stands alone.
112    Punct(char),
113    /// A text literal's text.
114    Text(String),
115    /// A numeric literal, exactly as written.
116    Number(String),
117    /// A byte-string literal's material.
118    ByteText(Vec<u8>),
119    /// One character literal's character.
120    Character(char),
121    /// One byte literal's byte.
122    Byte(u8),
123    /// A C string literal's material without its terminating NUL.
124    NulTerminatedText(Vec<u8>),
125    /// A raw identifier's name without its `r#` spelling marker.
126    RawIdentifier(String),
127    /// One punctuation character joined to the token after it.
128    JointPunct(char),
129}
130
131/// What one captured token carries.
132///
133/// 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.
134///
135/// # Ordering
136///
137/// 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.
138#[derive(Debug, Clone, PartialEq, Eq, Hash)]
139pub enum CapturedPayload {
140    /// An ordinary identifier-shaped word or keyword.
141    Word(String),
142    /// One punctuation character that stands alone.
143    Punct(char),
144    /// A text literal's text: `"…"` and `r"…"` alike, escapes read and quotes removed.
145    Text(String),
146    /// 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.
147    Number(String),
148    /// A delimited group and the tokens inside it.
149    Group {
150        /// The delimiter written around the group.
151        delimiter: CapturedDelimiter,
152        /// The tokens inside, in the order they were written.
153        trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
154    },
155    /// 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.
156    ByteText(Vec<u8>),
157    /// One character literal's character: `'…'`.
158    Character(char),
159    /// One byte literal's byte: `b'…'`.
160    Byte(u8),
161    /// A C string literal's material: `c"…"` and `cr"…"`, without the terminating NUL, which is the literal form's and never the value's.
162    NulTerminatedText(Vec<u8>),
163    /// A raw identifier's name without its `r#` spelling marker.
164    RawIdentifier(String),
165    /// One punctuation character joined to the token after it.
166    JointPunct(char),
167}
168
169crate::roster! {
170    /// Why one literal spelling could not be read into the value it names.
171    ///
172    /// 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.
173    #[must_use = "a literal refusal names why the spelling could not be read into a value"]
174    pub enum LiteralReadCause {
175        /// The spelling opens with no literal form this grammar has a row for.
176        NotAKnownForm = "not-a-known-form",
177        /// The form is one this grammar reads, and its body carries material this grammar could not read the value of.
178        NotReadable = "not-readable",
179    }
180}
181
182/// One captured token: what it carries, where it sits, and how to reach the compiler span it came from.
183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
184pub struct CapturedTokenTree {
185    payload: CapturedPayload,
186    path: TokenPath,
187    span: SpanHandle,
188}
189
190/// One captured declared input: the top-level token trees, and how many span handles the producer issued.
191#[derive(Debug, Clone, PartialEq, Eq, Hash)]
192pub struct CapturedInput {
193    trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
194    issued: usize,
195}
196
197/// One borrowed run of captured tokens under its original source boundary.
198///
199/// A fragment is a view into one [`CapturedInput`] or one captured group, never a second token tree.
200/// Its producer spans remain on the tokens it borrows, while its canonical bytes exclude those producer-local coordinates on the same terms as the complete capture.
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
202pub struct CapturedFragment<'tokens> {
203    pub(super) tokens: &'tokens [CapturedTokenTree],
204    pub(super) end: Option<SpanHandle>,
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
208enum CaptureBuilderStanding {
209    Ready,
210    Refused { retained_before_capture: usize },
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
214enum CaptureLevelStanding {
215    Open,
216    Finished,
217}
218
219/// The only state that issues capture handles and retains the producer's matching source positions.
220#[derive(Debug, Clone, PartialEq, Eq, Hash)]
221pub struct CaptureBuilder<Position> {
222    positions: Vec<Position>,
223    walk: CaptureWalk,
224    standing: CaptureBuilderStanding,
225}
226
227/// One nesting level borrowed from a [`CaptureBuilder`].
228///
229/// A producer can append atoms or groups and cannot state a path, a handle, or a denominator.
230/// Every operation consumes the level, and only a successful operation returns it, so a refused partial level cannot be finished.
231pub struct CaptureLevel<'capture, Position> {
232    positions: &'capture mut Vec<Position>,
233    walk: &'capture mut CaptureWalk,
234    builder_standing: &'capture mut CaptureBuilderStanding,
235    retained_before_capture: usize,
236    path: TokenPath,
237    trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
238    standing: CaptureLevelStanding,
239}
240
241/// Why a checked capture was not completed.
242#[must_use = "a capture refusal names whether a declared bound or the producer's own reading stopped construction"]
243#[derive(Debug, Clone, PartialEq, Eq, Hash)]
244pub enum CaptureBuildRefusal<Position, ProducerRefusal> {
245    /// One declared capture magnitude was exceeded at this producer position.
246    Unbounded {
247        /// The magnitude exceeded.
248        bound: CaptureBound,
249        /// The producer's own position for the token that reached it.
250        at: Position,
251    },
252    /// The producer could not read one token after the builder issued its declaration path and producer handle.
253    ProducerRefused {
254        /// The producer's typed reason.
255        cause: ProducerRefusal,
256        /// The declaration-local route to the token the producer could not read.
257        path: TokenPath,
258        /// The handle already bound to the token's retained source position.
259        at: SpanHandle,
260    },
261}
262
263/// Why one span table could not say where a handle sits.
264///
265/// 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.
266#[must_use = "a resolution refusal carries the handle and how far the table reaches"]
267#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
268pub struct SpanResolutionRefusal {
269    /// The handle the table was asked to resolve.
270    pub handle: SpanHandle,
271    /// How many positions the table carries; a handle at or past this index names no position in it.
272    pub reaches: usize,
273}
274
275/// How a producer answers "where is the token this handle names?".
276///
277/// 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.
278#[derive(Debug, Clone, PartialEq, Eq, Hash)]
279pub enum SpanTable {
280    /// Byte offsets into the declared input, one per issued handle.
281    ByteOffsets(Bounded<u64, CAPTURED_TREE_TOKEN_LIMIT>),
282    /// The producer holds the compiler's spans and resolves handles itself.
283    ProducerHeld,
284}