use lispexp::{lex, Delim, Options, TokenKind, UnterminatedKind};
#[test]
fn tokens_tile_the_input() {
let src = "(a ; c\n \"s\")";
let opts = Options::scheme();
let tokens: Vec<_> = lex(src, &opts).collect();
let mut cursor = 0u32;
for t in &tokens {
assert_eq!(t.span.start, cursor, "gap or overlap at {:?}", t);
cursor = t.span.end;
}
assert_eq!(cursor as usize, src.len(), "did not cover the whole input");
}
#[test]
fn comments_and_strings_are_surfaced() {
let src = "a ; comment\n#| block |# \"str\"";
let opts = Options::scheme();
let kinds: Vec<TokenKind> = lex(src, &opts).map(|t| t.kind).collect();
assert!(kinds.contains(&TokenKind::LineComment));
assert!(kinds.contains(&TokenKind::BlockComment));
assert!(kinds.contains(&TokenKind::Str));
}
#[test]
fn delimiters_carry_their_shape() {
let src = "([{";
let mut opts = Options::scheme();
opts.curly = lispexp::DelimRole::List;
let kinds: Vec<TokenKind> = lex(src, &opts).map(|t| t.kind).collect();
assert_eq!(
kinds,
vec![
TokenKind::Open(Delim::Round),
TokenKind::Open(Delim::Square),
TokenKind::Open(Delim::Curly),
]
);
}
#[test]
fn char_literal_delimiters_do_not_miscount_as_parens() {
let src = r"#\(";
let opts = Options::scheme();
let tokens: Vec<_> = lex(src, &opts).collect();
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].kind, TokenKind::Char);
}
fn assert_unterminated(src: &str, opts: &Options, kind: UnterminatedKind) {
let tokens: Vec<_> = lex(src, opts).collect();
assert_eq!(tokens.len(), 1, "expected one token, got {tokens:?}");
assert_eq!(tokens[0].kind, TokenKind::Unterminated(kind));
assert_eq!(tokens[0].span.start, 0);
assert_eq!(
tokens[0].span.end as usize,
src.len(),
"tiling invariant broken"
);
}
#[test]
fn unterminated_string_is_str_kind() {
assert_unterminated(r#""abc"#, &Options::scheme(), UnterminatedKind::Str);
}
#[test]
fn unterminated_piped_symbol_is_piped_symbol_kind() {
assert_unterminated("|ab", &Options::scheme(), UnterminatedKind::PipedSymbol);
}
#[test]
fn unterminated_guile_extended_symbol_is_piped_symbol_kind() {
assert_unterminated("#{ab", &Options::guile(), UnterminatedKind::PipedSymbol);
}
#[test]
fn unterminated_block_comment_is_block_comment_kind_with_depth() {
let mut opts = Options::scheme();
opts.block_comment = opts.block_comment.map(|bc| lispexp::BlockComment {
nestable: false,
..bc
});
assert_unterminated("#| a", &opts, UnterminatedKind::BlockComment { depth: 1 });
assert_unterminated(
"#| a #| b",
&Options::scheme(),
UnterminatedKind::BlockComment { depth: 2 },
);
}
#[test]
fn unterminated_janet_long_string_is_long_string_kind() {
assert_unterminated("`abc", &Options::janet(), UnterminatedKind::LongString);
}
#[test]
fn unterminated_hy_bracket_string_is_bracket_string_kind() {
assert_unterminated("#[[x", &Options::hy(), UnterminatedKind::BracketString);
assert_unterminated("#[[x]y", &Options::hy(), UnterminatedKind::BracketString);
}
#[test]
fn unterminated_char_set_is_char_set_kind() {
assert_unterminated(
"#[a-z",
&Options::scheme_superset(),
UnterminatedKind::CharSet,
);
}
#[test]
fn unterminated_regex_slash_is_regex_kind() {
assert_unterminated("#/re", &Options::scheme_superset(), UnterminatedKind::Regex);
}