use core::fmt;
use intern_lang::Symbol;
use span_lang::{Span, Spanned};
use crate::TokenKind;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Token<K> {
pub span: Span,
pub kind: K,
}
impl<K> Token<K> {
#[inline]
#[must_use]
pub const fn new(kind: K, span: Span) -> Self {
Self { span, kind }
}
#[inline]
#[must_use]
pub const fn kind(&self) -> &K {
&self.kind
}
#[inline]
#[must_use]
pub const fn span(&self) -> Span {
self.span
}
#[inline]
#[must_use]
pub fn into_kind(self) -> K {
self.kind
}
#[inline]
#[must_use]
pub fn map<U>(self, f: impl FnOnce(K) -> U) -> Token<U> {
Token {
span: self.span,
kind: f(self.kind),
}
}
#[inline]
#[must_use]
pub fn as_ref(&self) -> Token<&K> {
Token {
span: self.span,
kind: &self.kind,
}
}
}
impl<K: TokenKind> Token<K> {
#[inline]
#[must_use]
pub fn is_trivia(&self) -> bool {
self.kind.is_trivia()
}
#[inline]
#[must_use]
pub fn is_eof(&self) -> bool {
self.kind.is_eof()
}
#[inline]
#[must_use]
pub fn symbol(&self) -> Option<Symbol> {
self.kind.symbol()
}
}
impl<K: fmt::Display> fmt::Display for Token<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} @ {}", self.kind, self.span)
}
}
impl<K> From<(K, Span)> for Token<K> {
#[inline]
fn from((kind, span): (K, Span)) -> Self {
Self::new(kind, span)
}
}
impl<K> From<Token<K>> for Spanned<K> {
#[inline]
fn from(token: Token<K>) -> Self {
Spanned::new(token.span, token.kind)
}
}
impl<K> From<Spanned<K>> for Token<K> {
#[inline]
fn from(spanned: Spanned<K>) -> Self {
Self::new(spanned.value, spanned.span)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
extern crate alloc;
use alloc::string::{String, ToString};
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Kind {
Ident(Symbol),
Plus,
Space,
Eof,
}
impl TokenKind for Kind {
fn is_trivia(&self) -> bool {
matches!(self, Kind::Space)
}
fn is_eof(&self) -> bool {
matches!(self, Kind::Eof)
}
fn symbol(&self) -> Option<Symbol> {
match self {
Kind::Ident(s) => Some(*s),
_ => None,
}
}
}
#[test]
fn test_new_stores_kind_and_span() {
let tok = Token::new(Kind::Plus, Span::new(2, 3));
assert_eq!(*tok.kind(), Kind::Plus);
assert_eq!(tok.span(), Span::new(2, 3));
}
#[test]
fn test_map_keeps_span() {
let tok = Token::new("123", Span::new(4, 7));
let mapped = tok.map(|s| s.len());
assert_eq!(*mapped.kind(), 3);
assert_eq!(mapped.span(), Span::new(4, 7));
}
#[test]
fn test_as_ref_does_not_consume() {
let owned = Token::new(String::from("name"), Span::new(0, 4));
let len = owned.as_ref().map(String::len);
assert_eq!(*len.kind(), 4);
assert_eq!(owned.kind(), "name");
}
#[test]
fn test_orders_by_span_first() {
let a = Token::new(Kind::Eof, Span::new(0, 1));
let b = Token::new(Kind::Plus, Span::new(1, 2));
assert!(a < b);
}
#[test]
fn test_delegators_forward_to_kind() {
let sym = Symbol::from_u32(3).unwrap();
assert!(Token::new(Kind::Space, Span::empty(0)).is_trivia());
assert!(Token::new(Kind::Eof, Span::empty(0)).is_eof());
assert_eq!(
Token::new(Kind::Ident(sym), Span::new(0, 1)).symbol(),
Some(sym)
);
assert_eq!(Token::new(Kind::Plus, Span::new(0, 1)).symbol(), None);
}
#[test]
fn test_display_reads_as_kind_at_span() {
let tok = Token::new("if", Span::new(0, 2));
assert_eq!(tok.to_string(), "if @ 0..2");
}
#[test]
fn test_roundtrips_through_spanned() {
let tok = Token::new(Kind::Plus, Span::new(2, 3));
let spanned: Spanned<Kind> = tok.into();
assert_eq!(spanned.span, Span::new(2, 3));
assert_eq!(spanned.value, Kind::Plus);
assert_eq!(Token::from(spanned), tok);
}
#[test]
fn test_from_tuple_matches_new() {
let from_tuple: Token<Kind> = (Kind::Plus, Span::new(0, 1)).into();
assert_eq!(from_tuple, Token::new(Kind::Plus, Span::new(0, 1)));
}
#[test]
fn test_token_is_copy_when_kind_is() {
let tok = Token::new(Kind::Plus, Span::new(0, 1));
let copy = tok;
assert_eq!(tok, copy);
}
}