use lispexp::{parse, Datum, DatumKind, Options, Prefix};
fn guile(src: &str) -> Vec<Datum<'_>> {
let parsed = parse(src, &Options::guile());
assert!(
parsed.errors.is_empty(),
"unexpected errors: {:?}",
parsed.errors
);
parsed.data
}
#[test]
fn hash_colon_keyword() {
let data = guile("#:kw");
assert_eq!(data[0].kind, DatumKind::Keyword("#:kw"));
}
#[test]
fn hash_apostrophe_is_var_quote() {
let data = guile("#'x");
assert!(matches!(
data[0].kind,
DatumKind::Prefixed {
prefix: Prefix::VarQuote,
..
}
));
}
#[test]
fn booleans() {
let data = guile("#t #f");
assert_eq!(data[0].kind, DatumKind::Bool(true));
assert_eq!(data[1].kind, DatumKind::Bool(false));
}
#[test]
fn char_literal() {
let data = guile(r"#\a");
assert_eq!(data[0].kind, DatumKind::Char(r"#\a"));
}
#[test]
fn nestable_block_comment_is_skipped() {
let data = guile("#| a #| nested |# b |# (define x 1)");
assert_eq!(data.len(), 1);
let DatumKind::List { items, .. } = &data[0].kind else {
panic!()
};
assert_eq!(items[0].kind, DatumKind::Symbol("define"));
assert_eq!(items[1].kind, DatumKind::Symbol("x"));
assert_eq!(items[2].kind, DatumKind::Number("1"));
}
#[test]
fn define_list() {
let data = guile("(define (f x) (+ x 1))");
assert_eq!(data.len(), 1);
assert!(matches!(data[0].kind, DatumKind::List { .. }));
}
#[test]
fn hash_curly_extended_symbol_is_one_symbol() {
let data = guile("#{foo bar}#");
assert_eq!(data.len(), 1, "expected one symbol, got {:?}", data);
assert_eq!(data[0].kind, DatumKind::Symbol("#{foo bar}#"));
}
#[test]
fn hash_curly_symbol_tiles_the_input() {
use lispexp::{lex, Options};
let src = "#{a b}# x";
let opts = Options::guile();
let mut cursor = 0u32;
for t in lex(src, &opts) {
assert_eq!(t.span.start, cursor, "gap/overlap at {t:?}");
cursor = t.span.end;
}
assert_eq!(cursor as usize, src.len());
let data = guile(src);
assert_eq!(data[0].kind, DatumKind::Symbol("#{a b}#"));
assert_eq!(data[1].kind, DatumKind::Symbol("x"));
}