use core::fmt::Display;
use alloc::string::String;
#[derive(Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub enum TokenKind {
None,
Equal,
Colon,
ColonEqual,
Plus,
PlusEqual,
Minus,
MinusEqual,
Star,
StarEqual,
Slash,
SlashEqual,
Semicolon,
Ampersand,
Pipe,
Caret,
Tilde,
And,
Or,
Bang,
BangEqual,
EqualEqual,
Less,
LessEqual,
Greater,
GreaterEqual,
QMark,
LeftParen,
RightParen,
Nil,
True,
False,
Print,
Ident,
FloatNumber,
HexNumber,
IntNumber,
String,
Enum,
}
impl Display for TokenKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::None => write!(f, "NONE"),
Self::Equal => write!(f, "="),
Self::Colon => write!(f, ":"),
Self::ColonEqual => write!(f, ":="),
Self::Plus => write!(f, "+"),
Self::PlusEqual => write!(f, "+="),
Self::Minus => write!(f, "-"),
Self::MinusEqual => write!(f, "-="),
Self::Star => write!(f, "*"),
Self::StarEqual => write!(f, "*="),
Self::Slash => write!(f, "/"),
Self::SlashEqual => write!(f, "/="),
Self::Semicolon => write!(f, ";"),
Self::Ampersand => write!(f, "&"),
Self::Pipe => write!(f, "|"),
Self::Caret => write!(f, "^"),
Self::Tilde => write!(f, "~"),
Self::And => write!(f, "&&"),
Self::Or => write!(f, "||"),
Self::Bang => write!(f, "!"),
Self::BangEqual => write!(f, "!="),
Self::EqualEqual => write!(f, "=="),
Self::Less => write!(f, "<"),
Self::LessEqual => write!(f, "<="),
Self::Greater => write!(f, ">"),
Self::GreaterEqual => write!(f, ">="),
Self::QMark => write!(f, "?"),
Self::LeftParen => write!(f, "("),
Self::RightParen => write!(f, ")"),
Self::Nil => write!(f, "'nil'"),
Self::True => write!(f, "'true'"),
Self::Print => write!(f, "'print'"),
Self::False => write!(f, "'false'"),
Self::Ident => write!(f, "an 'Ident'"),
Self::FloatNumber => write!(f, "a 'FloatNumber'"),
Self::HexNumber => write!(f, "a 'HexNumber'"),
Self::IntNumber => write!(f, "a 'IntNumber'"),
Self::String => write!(f, "a 'String'"),
Self::Enum => write!(f, "an 'Enum'"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token {
pub(crate) origin: String,
pub(crate) offset: usize,
pub(crate) line: usize,
pub(crate) kind: TokenKind,
}
impl Token {
#[inline]
pub const fn kind(&self) -> TokenKind {
self.kind
}
pub fn none() -> Self {
Self {
origin: String::default(),
offset: 0,
line: 0,
kind: TokenKind::None,
}
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<&Token>();
is_normal::<Token>();
is_normal::<&TokenKind>();
is_normal::<TokenKind>();
}
#[test]
fn token_kind_display_covers_every_variant() {
let cases: &[(TokenKind, &str)] = &[
(TokenKind::None, "NONE"),
(TokenKind::Equal, "="),
(TokenKind::Colon, ":"),
(TokenKind::ColonEqual, ":="),
(TokenKind::Plus, "+"),
(TokenKind::PlusEqual, "+="),
(TokenKind::Minus, "-"),
(TokenKind::MinusEqual, "-="),
(TokenKind::Star, "*"),
(TokenKind::StarEqual, "*="),
(TokenKind::Slash, "/"),
(TokenKind::SlashEqual, "/="),
(TokenKind::Semicolon, ";"),
(TokenKind::Ampersand, "&"),
(TokenKind::Pipe, "|"),
(TokenKind::Caret, "^"),
(TokenKind::Tilde, "~"),
(TokenKind::And, "&&"),
(TokenKind::Or, "||"),
(TokenKind::Bang, "!"),
(TokenKind::BangEqual, "!="),
(TokenKind::EqualEqual, "=="),
(TokenKind::Less, "<"),
(TokenKind::LessEqual, "<="),
(TokenKind::Greater, ">"),
(TokenKind::GreaterEqual, ">="),
(TokenKind::QMark, "?"),
(TokenKind::LeftParen, "("),
(TokenKind::RightParen, ")"),
(TokenKind::Nil, "'nil'"),
(TokenKind::True, "'true'"),
(TokenKind::Print, "'print'"),
(TokenKind::False, "'false'"),
(TokenKind::Ident, "an 'Ident'"),
(TokenKind::FloatNumber, "a 'FloatNumber'"),
(TokenKind::HexNumber, "a 'HexNumber'"),
(TokenKind::IntNumber, "a 'IntNumber'"),
(TokenKind::String, "a 'String'"),
(TokenKind::Enum, "an 'Enum'"),
];
for (kind, expected) in cases {
assert_eq!(format!("{kind}"), *expected);
}
}
#[test]
fn token_none_constructor_returns_none_kind() {
let t = Token::none();
assert!(matches!(t.kind(), TokenKind::None));
assert_eq!(t.offset, 0);
assert_eq!(t.line, 0);
assert!(t.origin.is_empty());
}
}