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}