drawlang-syntax 0.1.2

Lexer, parser, lossless syntax tree, and formatter for the drawlang DSL
Documentation
use drawlang_syntax::fmt::format;
use drawlang_syntax::parse_source;

fn example(name: &str) -> String {
    let path = format!("{}/../../examples/{name}", env!("CARGO_MANIFEST_DIR"));
    std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {path}: {e}"))
}

fn fmt(src: &str) -> String {
    let parsed = parse_source(src);
    assert!(!parsed.has_errors(), "{:?}", parsed.diagnostics);
    format(&parsed.file, src)
}

#[test]
fn idempotent_on_examples() {
    for name in ["hello.drawl", "gpu-topology.drawl", "cpu-arch.drawl"] {
        let src = example(name);
        let once = fmt(&src);
        let twice = fmt(&once);
        assert_eq!(once, twice, "{name}: fmt must be idempotent");
    }
}

#[test]
fn formatted_output_still_parses_clean() {
    for name in ["hello.drawl", "gpu-topology.drawl", "cpu-arch.drawl"] {
        let formatted = fmt(&example(name));
        let reparsed = parse_source(&formatted);
        assert!(
            reparsed.diagnostics.is_empty(),
            "{name}: formatted output must parse clean:\n{formatted}"
        );
    }
}

#[test]
fn comments_round_trip() {
    let src = "drawl 0.1\n\n// leading comment\nn { label: \"x\" } // trailing\n";
    let out = fmt(src);
    assert!(out.contains("// leading comment"), "{out}");
    assert!(out.contains("// trailing"), "{out}");
}

#[test]
fn strings_and_interpolation_round_trip() {
    let src = "drawl 0.1\nfor i in 0..2 { n { label: \"GPU {i + 1} \\\"x\\\"\" } }\n";
    let out = fmt(src);
    assert!(out.contains(r#""GPU {i + 1} \"x\"""#), "{out}");
    let reparsed = parse_source(&out);
    assert!(
        reparsed.diagnostics.is_empty(),
        "{:?}",
        reparsed.diagnostics
    );
}

#[test]
fn normalizes_messy_spacing() {
    let messy = "drawl 0.1\na{label:\"A\"}\nb   {   label : \"B\"  }\na->b:\"edge\"\n";
    let out = fmt(messy);
    assert!(out.contains("a { label: \"A\" }"), "{out}");
    assert!(out.contains("a -> b : \"edge\""), "{out}");
}