use crate::span::Span;
#[derive(Debug, Clone, PartialEq)]
pub enum TokenKind {
Int(i64),
Float(f64),
Byte(u8),
Str(String),
StrStart(String),
StrMid(String),
StrEnd(String),
InterpStart,
InterpEnd,
Ident(String),
Fn,
Let,
Mut,
If,
Else,
While,
For,
In,
Return,
Break,
Continue,
Defer,
Match,
Struct,
Enum,
Interface,
Comptime,
Is,
Pure,
Io,
Alloc,
Panic,
Or,
SelfKw,
True,
False,
I64Ty,
F64Ty,
BoolTy,
StrTy,
ByteTy,
VoidTy,
Plus,
Minus,
Star,
Slash,
Percent,
EqEq,
BangEq,
Lt,
LtEq,
Gt,
GtEq,
AmpAmp,
PipePipe,
Bang,
Eq,
Dot,
Comma,
Colon,
Semi,
LParen,
RParen,
LBracket,
RBracket,
LBrace,
RBrace,
Arrow,
FatArrow,
PipeGt,
Question,
DotDot,
DotDotEq,
Eof,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
}
impl Token {
pub fn new(kind: TokenKind, span: Span) -> Self {
Token { kind, span }
}
}
pub fn keyword(ident: &str) -> Option<TokenKind> {
use TokenKind::*;
Some(match ident {
"fn" => Fn,
"let" => Let,
"mut" => Mut,
"if" => If,
"else" => Else,
"while" => While,
"for" => For,
"in" => In,
"return" => Return,
"break" => Break,
"continue" => Continue,
"defer" => Defer,
"match" => Match,
"struct" => Struct,
"enum" => Enum,
"interface" => Interface,
"comptime" => Comptime,
"is" => Is,
"pure" => Pure,
"io" => Io,
"alloc" => Alloc,
"panic" => Panic,
"or" => Or,
"self" => SelfKw,
"true" => True,
"false" => False,
"i64" => I64Ty,
"f64" => F64Ty,
"bool" => BoolTy,
"str" => StrTy,
"byte" => ByteTy,
"void" => VoidTy,
_ => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reserved_words_lex_to_their_keyword_kind() {
let cases: &[(&str, TokenKind)] = &[
("fn", TokenKind::Fn),
("let", TokenKind::Let),
("mut", TokenKind::Mut),
("if", TokenKind::If),
("else", TokenKind::Else),
("while", TokenKind::While),
("for", TokenKind::For),
("in", TokenKind::In),
("return", TokenKind::Return),
("break", TokenKind::Break),
("continue", TokenKind::Continue),
("defer", TokenKind::Defer),
("match", TokenKind::Match),
("struct", TokenKind::Struct),
("enum", TokenKind::Enum),
("interface", TokenKind::Interface),
("comptime", TokenKind::Comptime),
("is", TokenKind::Is),
("pure", TokenKind::Pure),
("io", TokenKind::Io),
("alloc", TokenKind::Alloc),
("panic", TokenKind::Panic),
("or", TokenKind::Or),
("self", TokenKind::SelfKw),
];
for (src, expected) in cases {
assert_eq!(keyword(src), Some(expected.clone()), "keyword({src:?})");
}
}
#[test]
fn true_and_false_are_boolean_keyword_kinds_not_idents() {
assert_eq!(keyword("true"), Some(TokenKind::True));
assert_eq!(keyword("false"), Some(TokenKind::False));
}
#[test]
fn primitive_type_names_are_keyword_kinds() {
assert_eq!(keyword("i64"), Some(TokenKind::I64Ty));
assert_eq!(keyword("f64"), Some(TokenKind::F64Ty));
assert_eq!(keyword("bool"), Some(TokenKind::BoolTy));
assert_eq!(keyword("str"), Some(TokenKind::StrTy));
assert_eq!(keyword("byte"), Some(TokenKind::ByteTy));
assert_eq!(keyword("void"), Some(TokenKind::VoidTy));
}
#[test]
fn stdlib_and_result_family_names_are_not_keywords() {
let not_keywords = [
"Result", "Option", "Ok", "Err", "Some", "None", "print", "println", "len", "push",
"pop", "sqrt", "abs", "assert", "type_of", "open", "close", "map", "filter", "reduce",
];
for name in not_keywords {
assert_eq!(keyword(name), None, "{name:?} must not be a keyword");
}
}
#[test]
fn ordinary_identifiers_are_not_keywords() {
for name in ["foo", "_x", "_", "x1", "__", "fooBar", "Fn", "LET"] {
assert_eq!(keyword(name), None, "{name:?} must not be a keyword");
}
}
#[test]
fn a_token_pairs_a_kind_with_a_span() {
let tok = Token::new(TokenKind::Plus, Span::new(3, 1));
assert_eq!(tok.kind, TokenKind::Plus);
assert_eq!(tok.span, Span::new(3, 1));
}
#[test]
fn there_is_an_eof_kind() {
let eof = Token::new(TokenKind::Eof, Span::new(0, 0));
assert_eq!(eof.kind, TokenKind::Eof);
}
#[test]
fn literal_kinds_carry_their_payload() {
assert_eq!(TokenKind::Int(42), TokenKind::Int(42));
assert_ne!(TokenKind::Int(42), TokenKind::Int(43));
assert_eq!(TokenKind::Byte(b'A'), TokenKind::Byte(65));
assert_eq!(
TokenKind::Str("abc".to_string()),
TokenKind::Str("abc".to_string())
);
assert_eq!(
TokenKind::StrStart("hi ".to_string()),
TokenKind::StrStart("hi ".to_string())
);
assert_eq!(
TokenKind::Ident("x".to_string()),
TokenKind::Ident("x".to_string())
);
assert_eq!(TokenKind::Float(1.5), TokenKind::Float(1.5));
}
}