Skip to main content

token_lang/
token.rs

1//! The [`Token`] type: a syntactic kind paired with the span it covers.
2
3use core::fmt;
4
5use intern_lang::Symbol;
6use span_lang::{Span, Spanned};
7
8use crate::TokenKind;
9
10/// A single lexical token: a [`kind`](Token::kind) paired with the [`Span`] of
11/// source it covers.
12///
13/// `Token<K>` is the shared seam between a lexer and a parser. The lexer produces
14/// a stream of `Token<K>`; the parser consumes it. Neither needs to know the
15/// other's internals — they agree only on this pair. The *kind* `K` is the
16/// language's own classification (an `enum` of keywords, punctuation, literals,
17/// and so on); token-lang stays language-agnostic by leaving `K` to the language
18/// and owning only the pairing with a span.
19///
20/// A token is a *classified span*: `K` says **what** was lexed, the [`Span`] says
21/// **where**. The type is `Copy` whenever `K` is, so a token whose kind is a plain
22/// enum (a discriminant plus an eight-byte span) is cheap to pass by value.
23///
24/// `Token<K>` mirrors [`Spanned<K>`](span_lang::Spanned) but carries token
25/// semantics: it orders by span first, so a slice of tokens sorts into source
26/// order; its `Display` reads as `kind @ start..end`; and when `K: TokenKind` it
27/// forwards the classification queries — [`is_trivia`](Token::is_trivia),
28/// [`is_eof`](Token::is_eof), and [`symbol`](Token::symbol) — to the kind. Convert
29/// between the two with the [`From`] impls when a layer wants the plain pair.
30///
31/// # Examples
32///
33/// ```
34/// use token_lang::{Span, Token, TokenKind};
35///
36/// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
37/// enum Kind {
38///     Ident,
39///     Plus,
40///     Eof,
41/// }
42/// impl TokenKind for Kind {
43///     fn is_eof(&self) -> bool {
44///         matches!(self, Kind::Eof)
45///     }
46/// }
47///
48/// // `a + b` lexed into tokens, then the end marker.
49/// let a = Token::new(Kind::Ident, Span::new(0, 1));
50/// let plus = Token::new(Kind::Plus, Span::new(2, 3));
51/// let eof = Token::new(Kind::Eof, Span::new(5, 5));
52///
53/// assert_eq!(*a.kind(), Kind::Ident);
54/// assert_eq!(plus.span(), Span::new(2, 3));
55/// assert!(eof.is_eof());
56///
57/// // Tokens are `Copy` and sort into source order.
58/// let mut stream = [eof, a, plus];
59/// stream.sort();
60/// assert_eq!(stream, [a, plus, eof]);
61/// ```
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
64pub struct Token<K> {
65    /// The half-open source range this token covers.
66    ///
67    /// Declared before [`kind`](Token::kind) so the derived ordering compares the
68    /// span first, sorting a slice of tokens into source order.
69    pub span: Span,
70    /// The language-specific kind of this token.
71    pub kind: K,
72}
73
74impl<K> Token<K> {
75    /// Pairs a kind with the span it was lexed from.
76    ///
77    /// `const`, so a token can initialise a `const` or `static` table — useful for
78    /// the fixed marker tokens (an end-of-input, a synthetic delimiter) a lexer
79    /// hands out.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use token_lang::{Span, Token};
85    ///
86    /// let tok = Token::new("ident", Span::new(0, 5));
87    /// assert_eq!(*tok.kind(), "ident");
88    /// assert_eq!(tok.span(), Span::new(0, 5));
89    /// ```
90    #[inline]
91    #[must_use]
92    pub const fn new(kind: K, span: Span) -> Self {
93        Self { span, kind }
94    }
95
96    /// Borrows this token's kind.
97    ///
98    /// # Examples
99    ///
100    /// ```
101    /// use token_lang::{Span, Token};
102    ///
103    /// let tok = Token::new(42u8, Span::new(0, 1));
104    /// assert_eq!(*tok.kind(), 42);
105    /// ```
106    #[inline]
107    #[must_use]
108    pub const fn kind(&self) -> &K {
109        &self.kind
110    }
111
112    /// Returns this token's span.
113    ///
114    /// `Span` is `Copy`, so this hands back the range by value rather than
115    /// borrowing it.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use token_lang::{Span, Token};
121    ///
122    /// let tok = Token::new((), Span::new(3, 7));
123    /// assert_eq!(tok.span(), Span::new(3, 7));
124    /// ```
125    #[inline]
126    #[must_use]
127    pub const fn span(&self) -> Span {
128        self.span
129    }
130
131    /// Consumes the token, returning just its kind and dropping the span.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use token_lang::{Span, Token};
137    ///
138    /// let tok = Token::new(String::from("if"), Span::new(0, 2));
139    /// assert_eq!(tok.into_kind(), "if");
140    /// ```
141    #[inline]
142    #[must_use]
143    pub fn into_kind(self) -> K {
144        self.kind
145    }
146
147    /// Transforms the kind with `f`, keeping the span unchanged.
148    ///
149    /// This is how a layer lifts a token from one kind to another without losing
150    /// where it came from — for example, mapping a raw lexer kind onto a coarser
151    /// parser kind, or wrapping the kind in a richer type.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use token_lang::{Span, Token};
157    ///
158    /// let raw = Token::new("123", Span::new(4, 7));
159    /// let parsed = raw.map(|s| s.parse::<u32>().unwrap());
160    /// assert_eq!(*parsed.kind(), 123);
161    /// assert_eq!(parsed.span(), Span::new(4, 7));
162    /// ```
163    #[inline]
164    #[must_use]
165    pub fn map<U>(self, f: impl FnOnce(K) -> U) -> Token<U> {
166        Token {
167            span: self.span,
168            kind: f(self.kind),
169        }
170    }
171
172    /// Borrows the kind, yielding a `Token<&K>` with the same span.
173    ///
174    /// Mirrors [`Option::as_ref`]: it lets you inspect or [`map`](Token::map) the
175    /// kind without consuming the original token.
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use token_lang::{Span, Token};
181    ///
182    /// let owned = Token::new(String::from("name"), Span::new(0, 4));
183    /// let len = owned.as_ref().map(|s| s.len());
184    /// assert_eq!(*len.kind(), 4);
185    /// // `owned` is still usable.
186    /// assert_eq!(owned.kind(), "name");
187    /// ```
188    #[inline]
189    #[must_use]
190    pub fn as_ref(&self) -> Token<&K> {
191        Token {
192            span: self.span,
193            kind: &self.kind,
194        }
195    }
196}
197
198impl<K: TokenKind> Token<K> {
199    /// Whether this token's kind is trivia. Forwards to
200    /// [`TokenKind::is_trivia`].
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use token_lang::{Span, Token, TokenKind};
206    ///
207    /// #[derive(Clone, Copy)]
208    /// enum Kind {
209    ///     Word,
210    ///     Space,
211    /// }
212    /// impl TokenKind for Kind {
213    ///     fn is_trivia(&self) -> bool {
214    ///         matches!(self, Kind::Space)
215    ///     }
216    /// }
217    ///
218    /// assert!(Token::new(Kind::Space, Span::new(0, 1)).is_trivia());
219    /// assert!(!Token::new(Kind::Word, Span::new(1, 5)).is_trivia());
220    /// ```
221    #[inline]
222    #[must_use]
223    pub fn is_trivia(&self) -> bool {
224        self.kind.is_trivia()
225    }
226
227    /// Whether this token's kind is the end-of-input marker. Forwards to
228    /// [`TokenKind::is_eof`].
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use token_lang::{Span, Token, TokenKind};
234    ///
235    /// #[derive(Clone, Copy)]
236    /// enum Kind {
237    ///     Word,
238    ///     Eof,
239    /// }
240    /// impl TokenKind for Kind {
241    ///     fn is_eof(&self) -> bool {
242    ///         matches!(self, Kind::Eof)
243    ///     }
244    /// }
245    ///
246    /// assert!(Token::new(Kind::Eof, Span::empty(9)).is_eof());
247    /// assert!(!Token::new(Kind::Word, Span::new(0, 4)).is_eof());
248    /// ```
249    #[inline]
250    #[must_use]
251    pub fn is_eof(&self) -> bool {
252        self.kind.is_eof()
253    }
254
255    /// The interned lexeme this token carries, if any. Forwards to
256    /// [`TokenKind::symbol`].
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// use intern_lang::Interner;
262    /// use token_lang::{Span, Symbol, Token, TokenKind};
263    ///
264    /// #[derive(Clone, Copy)]
265    /// enum Kind {
266    ///     Ident(Symbol),
267    ///     Plus,
268    /// }
269    /// impl TokenKind for Kind {
270    ///     fn symbol(&self) -> Option<Symbol> {
271    ///         match self {
272    ///             Kind::Ident(s) => Some(*s),
273    ///             Kind::Plus => None,
274    ///         }
275    ///     }
276    /// }
277    ///
278    /// let mut interner = Interner::new();
279    /// let tok = Token::new(Kind::Ident(interner.intern("x")), Span::new(0, 1));
280    /// assert_eq!(tok.symbol().and_then(|s| interner.resolve(s)), Some("x"));
281    /// assert_eq!(Token::new(Kind::Plus, Span::new(1, 2)).symbol(), None);
282    /// ```
283    #[inline]
284    #[must_use]
285    pub fn symbol(&self) -> Option<Symbol> {
286        self.kind.symbol()
287    }
288}
289
290impl<K: fmt::Display> fmt::Display for Token<K> {
291    /// Formats as `kind @ start..end`.
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        write!(f, "{} @ {}", self.kind, self.span)
294    }
295}
296
297impl<K> From<(K, Span)> for Token<K> {
298    /// Builds a token from a `(kind, span)` pair, matching the
299    /// [`new`](Token::new) argument order.
300    #[inline]
301    fn from((kind, span): (K, Span)) -> Self {
302        Self::new(kind, span)
303    }
304}
305
306impl<K> From<Token<K>> for Spanned<K> {
307    /// A token is a spanned kind; this drops the token semantics for the plain
308    /// [`Spanned`] pair, preserving both fields.
309    #[inline]
310    fn from(token: Token<K>) -> Self {
311        Spanned::new(token.span, token.kind)
312    }
313}
314
315impl<K> From<Spanned<K>> for Token<K> {
316    /// The inverse of the token-to-[`Spanned`] conversion: reinterprets a spanned
317    /// value as a token of that kind, preserving both fields.
318    #[inline]
319    fn from(spanned: Spanned<K>) -> Self {
320        Self::new(spanned.value, spanned.span)
321    }
322}
323
324#[cfg(test)]
325mod tests {
326    // Reconstructing a `Symbol` from a known id is `Option`-returning; unwrapping
327    // it in a test where the id is a literal is the clearest form.
328    #![allow(clippy::unwrap_used)]
329
330    extern crate alloc;
331    use alloc::string::{String, ToString};
332
333    use super::*;
334
335    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
336    enum Kind {
337        Ident(Symbol),
338        Plus,
339        Space,
340        Eof,
341    }
342
343    impl TokenKind for Kind {
344        fn is_trivia(&self) -> bool {
345            matches!(self, Kind::Space)
346        }
347        fn is_eof(&self) -> bool {
348            matches!(self, Kind::Eof)
349        }
350        fn symbol(&self) -> Option<Symbol> {
351            match self {
352                Kind::Ident(s) => Some(*s),
353                _ => None,
354            }
355        }
356    }
357
358    #[test]
359    fn test_new_stores_kind_and_span() {
360        let tok = Token::new(Kind::Plus, Span::new(2, 3));
361        assert_eq!(*tok.kind(), Kind::Plus);
362        assert_eq!(tok.span(), Span::new(2, 3));
363    }
364
365    #[test]
366    fn test_map_keeps_span() {
367        let tok = Token::new("123", Span::new(4, 7));
368        let mapped = tok.map(|s| s.len());
369        assert_eq!(*mapped.kind(), 3);
370        assert_eq!(mapped.span(), Span::new(4, 7));
371    }
372
373    #[test]
374    fn test_as_ref_does_not_consume() {
375        let owned = Token::new(String::from("name"), Span::new(0, 4));
376        let len = owned.as_ref().map(String::len);
377        assert_eq!(*len.kind(), 4);
378        assert_eq!(owned.kind(), "name");
379    }
380
381    #[test]
382    fn test_orders_by_span_first() {
383        // Spans differ; the smaller span sorts first regardless of kind value.
384        let a = Token::new(Kind::Eof, Span::new(0, 1));
385        let b = Token::new(Kind::Plus, Span::new(1, 2));
386        assert!(a < b);
387    }
388
389    #[test]
390    fn test_delegators_forward_to_kind() {
391        let sym = Symbol::from_u32(3).unwrap();
392        assert!(Token::new(Kind::Space, Span::empty(0)).is_trivia());
393        assert!(Token::new(Kind::Eof, Span::empty(0)).is_eof());
394        assert_eq!(
395            Token::new(Kind::Ident(sym), Span::new(0, 1)).symbol(),
396            Some(sym)
397        );
398        assert_eq!(Token::new(Kind::Plus, Span::new(0, 1)).symbol(), None);
399    }
400
401    #[test]
402    fn test_display_reads_as_kind_at_span() {
403        let tok = Token::new("if", Span::new(0, 2));
404        assert_eq!(tok.to_string(), "if @ 0..2");
405    }
406
407    #[test]
408    fn test_roundtrips_through_spanned() {
409        let tok = Token::new(Kind::Plus, Span::new(2, 3));
410        let spanned: Spanned<Kind> = tok.into();
411        assert_eq!(spanned.span, Span::new(2, 3));
412        assert_eq!(spanned.value, Kind::Plus);
413        assert_eq!(Token::from(spanned), tok);
414    }
415
416    #[test]
417    fn test_from_tuple_matches_new() {
418        let from_tuple: Token<Kind> = (Kind::Plus, Span::new(0, 1)).into();
419        assert_eq!(from_tuple, Token::new(Kind::Plus, Span::new(0, 1)));
420    }
421
422    #[test]
423    fn test_token_is_copy_when_kind_is() {
424        let tok = Token::new(Kind::Plus, Span::new(0, 1));
425        let copy = tok;
426        // Both usable: `tok` was copied, not moved.
427        assert_eq!(tok, copy);
428    }
429}