use logos::Logos;
use std::fmt;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Logos)]
pub enum TokenKind {
#[regex("[ \n]+")]
Whitespace,
#[token("def")]
FnKw,
#[token("val")]
ValKw,
#[regex("[A-Za-z][A-Za-z0-9]*")]
Ident,
#[regex("[0-9]+")]
IntNumber,
#[regex("[0-9]+L")]
LongNumber,
#[token("+")]
Plus,
#[token("-")]
Minus,
#[token("*")]
Star,
#[token("/")]
Slash,
#[token("&&")]
And,
#[token("=")]
Equals,
#[token("(")]
LParen,
#[token(")")]
RParen,
#[token("{")]
LBrace,
#[token("}")]
RBrace,
#[regex("//.*")]
Comment,
#[error]
Error,
}
impl TokenKind {
pub fn is_trivia(self) -> bool {
matches!(self, Self::Whitespace | Self::Comment)
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Whitespace => "whitespace",
Self::FnKw => "‘def’",
Self::ValKw => "‘val’",
Self::Ident => "identifier",
Self::IntNumber => "number",
Self::LongNumber => "number",
Self::Plus => "‘+’",
Self::Minus => "‘-’",
Self::Star => "‘*’",
Self::Slash => "‘/’",
Self::And => "‘&&’",
Self::Equals => "‘=’",
Self::LParen => "‘(’",
Self::RParen => "‘)’",
Self::LBrace => "‘{’",
Self::RBrace => "‘}’",
Self::Comment => "comment",
Self::Error => "an unrecognized token",
})
}
}
#[cfg(test)]
mod tests {
use super::super::Lexer;
use super::*;
fn check(input: &str, kind: TokenKind) {
let mut lexer = Lexer::new(input);
let token = lexer.next().unwrap();
assert_eq!(token.kind, kind);
assert_eq!(token.text, input);
}
#[test]
fn lex_spaces_and_newlines() {
check(" \n ", TokenKind::Whitespace);
}
#[test]
fn lex_fn_keyword() {
check("def", TokenKind::FnKw);
}
#[test]
fn lex_val_keyword() {
check("val", TokenKind::ValKw);
}
#[test]
fn lex_alphabetic_identifier() {
check("abcd", TokenKind::Ident);
}
#[test]
fn lex_alphanumeric_identifier() {
check("ab123cde456", TokenKind::Ident);
}
#[test]
fn lex_mixed_case_identifier() {
check("ABCdef", TokenKind::Ident);
}
#[test]
fn lex_single_char_identifier() {
check("x", TokenKind::Ident);
}
#[test]
fn lex_number() {
check("123456", TokenKind::IntNumber);
}
#[test]
fn lex_plus() {
check("+", TokenKind::Plus);
}
#[test]
fn lex_minus() {
check("-", TokenKind::Minus);
}
#[test]
fn lex_star() {
check("*", TokenKind::Star);
}
#[test]
fn lex_slash() {
check("/", TokenKind::Slash);
}
#[test]
fn lex_equals() {
check("=", TokenKind::Equals);
}
#[test]
fn lex_left_parenthesis() {
check("(", TokenKind::LParen);
}
#[test]
fn lex_right_parenthesis() {
check(")", TokenKind::RParen);
}
#[test]
fn lex_left_brace() {
check("{", TokenKind::LBrace);
}
#[test]
fn lex_right_brace() {
check("}", TokenKind::RBrace);
}
#[test]
fn lex_comment() {
check("// foo", TokenKind::Comment);
}
}