macroonz-compiler 0.2.0

Deterministic Rust code generation for procedural macros: plan, render, close, explain, and bind one sealed expansion from declared input.
Documentation
//! The capture home's declarations: what one captured declaration is and how a producer's span table answers.
//!
//! Declarations only.
//! 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.

use crate::bounded::Bounded;

#[path = "type_guard.rs"]
mod guard;

/// Steps one token path may carry, and so how deeply a declared input may nest.
///
/// 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.
pub const TOKEN_PATH_DEPTH_LIMIT: usize = 32;

/// Token trees one captured input may carry at any one nesting level.
pub const CAPTURED_TOKEN_LIMIT: usize = 4096;

/// Tokens one captured input may retain across the whole tree, and positions one span table may hold.
///
/// The capture-work budget owns the denominator.
/// One complete nesting level remains available for examined material that does not become a retained token.
pub const CAPTURED_TREE_TOKEN_LIMIT: usize = CAPTURE_WORK_LIMIT - CAPTURED_TOKEN_LIMIT;

/// Units of capture work one walk may spend, one unit per examined token.
///
/// 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.
pub const CAPTURE_WORK_LIMIT: usize = 65_536;

/// An opaque index into the producer's span table.
///
/// 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.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpanHandle(u32);

/// The coordinate system one source position is counted in.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CoordinateRole {
    /// A zero-based byte offset in the captured text.
    Byte,
    /// A zero-based ordinal retained by the source producer.
    SemanticOrigin,
}

/// One compiler-local source position with its coordinate system stated.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceCoordinate {
    /// The coordinate system in which the position is counted.
    pub role: CoordinateRole,
    /// The zero-based position in that coordinate system.
    pub position: u64,
}

/// The delimiter one captured group is written with.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CapturedDelimiter {
    /// `( … )`.
    Parenthesis,
    /// `{ … }`.
    Brace,
    /// `[ … ]`.
    Bracket,
    /// A group with no delimiter written — the invisible grouping a compiler inserts around a captured fragment.
    ///
    /// 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.
    Bare,
}

crate::roster! {
    /// Which declared magnitude one capture ran past.
    ///
    /// 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.
    #[must_use = "a bound refusal names which declared magnitude the capture would have passed"]
    pub enum CaptureBound {
        /// The declared input nests deeper than the declared magnitude.
        Depth = "depth",
        /// One nesting level carries more token trees than the declared magnitude.
        Level = "level",
        /// The whole tree carries more tokens than the declared magnitude.
        Tree = "tree",
        /// The walk spent the declared capture-work budget.
        Work = "work",
    }
}

/// Where one captured token sits, as the index route from the root of the declared input.
///
/// 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.
/// 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.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TokenPath {
    steps: Bounded<u32, TOKEN_PATH_DEPTH_LIMIT>,
}

/// The running state of one capture walk: what the walk has spent, and how much of the whole-tree magnitude it has taken.
///
/// 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.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CaptureWalk {
    remaining: usize,
    taken: usize,
}

/// One non-group value a capture producer offers to the checked builder.
///
/// Groups have their own builder operation so no caller can smuggle child trees carrying foreign paths or handles through an atom seat.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CapturedAtom {
    /// An ordinary identifier-shaped word or keyword.
    Word(String),
    /// One punctuation character that stands alone.
    Punct(char),
    /// A text literal's text.
    Text(String),
    /// A numeric literal, exactly as written.
    Number(String),
    /// A byte-string literal's material.
    ByteText(Vec<u8>),
    /// One character literal's character.
    Character(char),
    /// One byte literal's byte.
    Byte(u8),
    /// A C string literal's material without its terminating NUL.
    NulTerminatedText(Vec<u8>),
    /// A raw identifier's name without its `r#` spelling marker.
    RawIdentifier(String),
    /// One punctuation character joined to the token after it.
    JointPunct(char),
}

/// What one captured token carries.
///
/// 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.
///
/// # Ordering
///
/// 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.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CapturedPayload {
    /// An ordinary identifier-shaped word or keyword.
    Word(String),
    /// One punctuation character that stands alone.
    Punct(char),
    /// A text literal's text: `"…"` and `r"…"` alike, escapes read and quotes removed.
    Text(String),
    /// 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.
    Number(String),
    /// A delimited group and the tokens inside it.
    Group {
        /// The delimiter written around the group.
        delimiter: CapturedDelimiter,
        /// The tokens inside, in the order they were written.
        trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
    },
    /// 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.
    ByteText(Vec<u8>),
    /// One character literal's character: `'…'`.
    Character(char),
    /// One byte literal's byte: `b'…'`.
    Byte(u8),
    /// A C string literal's material: `c"…"` and `cr"…"`, without the terminating NUL, which is the literal form's and never the value's.
    NulTerminatedText(Vec<u8>),
    /// A raw identifier's name without its `r#` spelling marker.
    RawIdentifier(String),
    /// One punctuation character joined to the token after it.
    JointPunct(char),
}

crate::roster! {
    /// Why one literal spelling could not be read into the value it names.
    ///
    /// 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.
    #[must_use = "a literal refusal names why the spelling could not be read into a value"]
    pub enum LiteralReadCause {
        /// The spelling opens with no literal form this grammar has a row for.
        NotAKnownForm = "not-a-known-form",
        /// The form is one this grammar reads, and its body carries material this grammar could not read the value of.
        NotReadable = "not-readable",
    }
}

/// One captured token: what it carries, where it sits, and how to reach the compiler span it came from.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CapturedTokenTree {
    payload: CapturedPayload,
    path: TokenPath,
    span: SpanHandle,
}

/// One captured declared input: the top-level token trees, and how many span handles the producer issued.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CapturedInput {
    trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
    issued: usize,
}

/// One borrowed run of captured tokens under its original source boundary.
///
/// A fragment is a view into one [`CapturedInput`] or one captured group, never a second token tree.
/// 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.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CapturedFragment<'tokens> {
    pub(super) tokens: &'tokens [CapturedTokenTree],
    pub(super) end: Option<SpanHandle>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum CaptureBuilderStanding {
    Ready,
    Refused { retained_before_capture: usize },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum CaptureLevelStanding {
    Open,
    Finished,
}

/// The only state that issues capture handles and retains the producer's matching source positions.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CaptureBuilder<Position> {
    positions: Vec<Position>,
    walk: CaptureWalk,
    standing: CaptureBuilderStanding,
}

/// One nesting level borrowed from a [`CaptureBuilder`].
///
/// A producer can append atoms or groups and cannot state a path, a handle, or a denominator.
/// Every operation consumes the level, and only a successful operation returns it, so a refused partial level cannot be finished.
pub struct CaptureLevel<'capture, Position> {
    positions: &'capture mut Vec<Position>,
    walk: &'capture mut CaptureWalk,
    builder_standing: &'capture mut CaptureBuilderStanding,
    retained_before_capture: usize,
    path: TokenPath,
    trees: Bounded<CapturedTokenTree, CAPTURED_TOKEN_LIMIT>,
    standing: CaptureLevelStanding,
}

/// Why a checked capture was not completed.
#[must_use = "a capture refusal names whether a declared bound or the producer's own reading stopped construction"]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CaptureBuildRefusal<Position, ProducerRefusal> {
    /// One declared capture magnitude was exceeded at this producer position.
    Unbounded {
        /// The magnitude exceeded.
        bound: CaptureBound,
        /// The producer's own position for the token that reached it.
        at: Position,
    },
    /// The producer could not read one token after the builder issued its declaration path and producer handle.
    ProducerRefused {
        /// The producer's typed reason.
        cause: ProducerRefusal,
        /// The declaration-local route to the token the producer could not read.
        path: TokenPath,
        /// The handle already bound to the token's retained source position.
        at: SpanHandle,
    },
}

/// Why one span table could not say where a handle sits.
///
/// 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.
#[must_use = "a resolution refusal carries the handle and how far the table reaches"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpanResolutionRefusal {
    /// The handle the table was asked to resolve.
    pub handle: SpanHandle,
    /// How many positions the table carries; a handle at or past this index names no position in it.
    pub reaches: usize,
}

/// How a producer answers "where is the token this handle names?".
///
/// 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.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SpanTable {
    /// Byte offsets into the declared input, one per issued handle.
    ByteOffsets(Bounded<u64, CAPTURED_TREE_TOKEN_LIMIT>),
    /// The producer holds the compiler's spans and resolves handles itself.
    ProducerHeld,
}