use intern_lang::Interner;
use token_lang::{Span, Spanned, Symbol, Token, TokenKind};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Kind {
Ident(Symbol),
Str(Symbol),
Plus,
Whitespace,
LineComment,
Eof,
}
impl TokenKind for Kind {
fn is_trivia(&self) -> bool {
matches!(self, Kind::Whitespace | Kind::LineComment)
}
fn is_eof(&self) -> bool {
matches!(self, Kind::Eof)
}
fn symbol(&self) -> Option<Symbol> {
match self {
Kind::Ident(s) | Kind::Str(s) => Some(*s),
_ => None,
}
}
}
fn lex(interner: &mut Interner) -> Vec<Token<Kind>> {
let ident = interner.intern("foo");
let literal = interner.intern("bar");
vec![
Token::new(Kind::Ident(ident), Span::new(0, 3)),
Token::new(Kind::Whitespace, Span::new(3, 4)),
Token::new(Kind::Plus, Span::new(4, 5)),
Token::new(Kind::Whitespace, Span::new(5, 6)),
Token::new(Kind::Str(literal), Span::new(6, 11)),
Token::new(Kind::LineComment, Span::new(11, 17)),
Token::new(Kind::Eof, Span::empty(17)),
]
}
#[test]
fn test_parser_skips_trivia_and_stops_at_eof() {
let mut interner = Interner::new();
let tokens = lex(&mut interner);
let significant: Vec<&Token<Kind>> = tokens
.iter()
.filter(|t| !t.is_trivia() && !t.is_eof())
.collect();
assert_eq!(significant.len(), 3);
assert_eq!(*significant[0].kind(), Kind::Ident(interner.intern("foo")));
assert_eq!(*significant[1].kind(), Kind::Plus);
assert!(tokens.last().unwrap().is_eof());
}
#[test]
fn test_symbols_resolve_back_to_source_text() {
let mut interner = Interner::new();
let tokens = lex(&mut interner);
let names: Vec<&str> = tokens
.iter()
.filter_map(|t| t.symbol())
.map(|sym| interner.resolve(sym).unwrap())
.collect();
assert_eq!(names, ["foo", "bar"]);
}
#[test]
fn test_tokens_sort_into_source_order() {
let mut interner = Interner::new();
let mut tokens = lex(&mut interner);
tokens.reverse();
tokens.sort();
for pair in tokens.windows(2) {
assert!(pair[0].span().start() <= pair[1].span().start());
}
assert!(tokens.last().unwrap().is_eof());
}
#[test]
fn test_span_covers_expected_range() {
let mut interner = Interner::new();
let tokens = lex(&mut interner);
let lit = tokens
.iter()
.find(|t| t.symbol().is_some() && t.span().start().to_u32() == 6);
let lit = lit.expect("string literal present");
assert_eq!(lit.span(), Span::new(6, 11));
assert_eq!(lit.span().len(), 5);
}
#[test]
fn test_map_rewrites_kind_keeping_span() {
let mut interner = Interner::new();
let tokens = lex(&mut interner);
let tagged: Vec<Token<&'static str>> = tokens
.into_iter()
.map(|t| {
let span = t.span();
let tag = match t.into_kind() {
Kind::Ident(_) => "ident",
Kind::Str(_) => "str",
Kind::Plus => "plus",
Kind::Whitespace => "ws",
Kind::LineComment => "comment",
Kind::Eof => "eof",
};
Token::new(tag, span)
})
.collect();
assert_eq!(*tagged[0].kind(), "ident");
assert_eq!(tagged[0].span(), Span::new(0, 3));
assert_eq!(*tagged.last().unwrap().kind(), "eof");
}
#[test]
fn test_spanned_conversions_round_trip() {
let tok = Token::new(Kind::Plus, Span::new(4, 5));
let spanned: Spanned<Kind> = tok.into();
assert_eq!(spanned, Spanned::new(Span::new(4, 5), Kind::Plus));
assert_eq!(Token::from(spanned), tok);
}
#[test]
fn test_display_renders_kind_at_span() {
let tok = Token::new("return", Span::new(10, 16));
assert_eq!(tok.to_string(), "return @ 10..16");
}