scheme-edit 0.2.0

Lossless Scheme S-expression parser and editor (toml_edit-style CST)
Documentation
use scheme_edit::{Document, ListKind, Node};

fn rt(src: &str) {
    let doc = Document::parse(src).unwrap();
    assert_eq!(doc.to_string(), src, "round-trip must be byte-identical");
}

#[test]
fn roundtrips_guile_reader_forms() {
    rt("#{a b}#");
    rt("#! c !#");
    rt("#vu8(1 2 3)");
    rt("#u8(1 2)");
    rt("#f32(1.0 2.0)");
    rt("#2((1 2)(3 4))");
    rt("(list #vu8(1 2) #{a b}# 3)");
}

#[test]
fn tagged_vector_tree_shape() {
    let doc = Document::parse("#vu8(1 2 3)").unwrap();
    let form = doc.forms().next().unwrap();
    match form {
        Node::List { kind, .. } => {
            assert_eq!(*kind, ListKind::TaggedVector("#vu8(".into()));
            assert_eq!(form.list_nodes().count(), 3);
        }
        other => panic!("expected List, got {other:?}"),
    }
}

#[test]
fn extended_symbol_tree_shape() {
    let doc = Document::parse("#{a b}#").unwrap();
    let form = doc.forms().next().unwrap();
    assert_eq!(*form, Node::Atom("#{a b}#".into()));
}

#[test]
fn roundtrips_simple() {
    rt("(list 1 2 3)");
}

#[test]
fn roundtrips_comments_and_weird_ws() {
    rt("; header\n(list\t(channel ; inline\n   (name 'guix)))\n\n");
}

#[test]
fn roundtrips_datum_comment() {
    rt("(list #;(channel (name 'old)) (channel (name 'new)))");
}

#[test]
fn roundtrips_dotted_pair_and_vector() {
    rt("(a . b) #(1 2)");
}

#[test]
fn roundtrips_gexp() {
    rt("#~(string-append #$coreutils \"/bin/ls\")");
}

#[test]
fn roundtrips_quasiquote() {
    rt("`(a ,b ,@(c d))");
}

#[test]
fn unbalanced_close_reports_position() {
    let err = Document::parse("(a))").unwrap_err();
    assert_eq!((err.line, err.col), (1, 4));
}

#[test]
fn unclosed_list_errors() {
    assert!(Document::parse("(a (b)").is_err());
}

#[test]
fn forms_skips_trivia() {
    let doc = Document::parse(";; c\n(use-modules (guix ci))\n\n(list)\n").unwrap();
    let heads: Vec<_> = doc.forms().filter_map(|n| n.head_symbol()).collect();
    assert_eq!(heads, vec!["use-modules", "list"]);
}