Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::{
    fmt::Write,
    fs,
    path::{Path, PathBuf},
};

use expect_test::expect_file;

use crate::LexedStr;

#[test]
fn lex_valid() {
    for case in TestCase::list("lexer/ok") {
        let actual = lex(&case.text);
        expect_file![case.txt].assert_eq(&actual)
    }
}

#[test]
fn lex_invalid() {
    for case in TestCase::list("lexer/err") {
        let actual = lex(&case.text);
        expect_file![case.txt].assert_eq(&actual)
    }
}

fn lex(text: &str) -> String {
    let lexed = LexedStr::new(text);

    let mut res = String::new();
    for i in 0..lexed.len() {
        let kind = lexed.kind(i);
        let text = lexed.text(i);
        let error = lexed.error(i);

        let error = error.map(|err| format!(" error: {}", err)).unwrap_or_default();
        writeln!(res, "{:?} {:?}{}", kind, text, error).unwrap();
    }
    res
}

#[test]
fn parse_valid() {
    for case in TestCase::list("parser/ok") {
        let (actual, errors) = parse(&case.text);
        assert!(!errors, "errors in an OK file {}:\n{}", case.rs.display(), actual);
        expect_file![case.txt].assert_eq(&actual);
    }
}

#[test]
fn parse_invalid() {
    for case in TestCase::list("parser/err") {
        let (actual, errors) = parse(&case.text);
        assert!(errors, "no errors in an ERR file {}:\n{}", case.rs.display(), actual);
        expect_file![case.txt].assert_eq(&actual)
    }
}

fn parse(text: &str) -> (String, bool) {
    let lexed = LexedStr::new(text);
    let input = lexed.to_input();
    let output = crate::parse_source_file(&input);

    let mut buf = String::new();
    let mut errors = Vec::new();
    let mut indent = String::new();
    lexed.intersperse_trivia(&output, false, &mut |step| match step {
        crate::StrStep::Token { kind, text } => {
            write!(buf, "{}", indent).unwrap();
            write!(buf, "{:?} {:?}\n", kind, text).unwrap();
        }
        crate::StrStep::Enter { kind } => {
            write!(buf, "{}", indent).unwrap();
            write!(buf, "{:?}\n", kind).unwrap();
            indent.push_str("  ");
        }
        crate::StrStep::Exit => {
            indent.pop();
            indent.pop();
        }
        crate::StrStep::Error { msg, pos } => errors.push(format!("error {}: {}\n", pos, msg)),
    });

    for (token, msg) in lexed.errors() {
        let pos = lexed.text_start(token);
        errors.push(format!("error {}: {}\n", pos, msg));
    }

    let has_errors = !errors.is_empty();
    for e in errors {
        buf.push_str(&e);
    }
    (buf, has_errors)
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct TestCase {
    rs: PathBuf,
    txt: PathBuf,
    text: String,
}

impl TestCase {
    fn list(path: &'static str) -> Vec<TestCase> {
        let crate_root_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
        let test_data_dir = crate_root_dir.join("test_data");
        let dir = test_data_dir.join(path);

        let mut res = Vec::new();
        let read_dir = fs::read_dir(&dir)
            .unwrap_or_else(|err| panic!("can't `read_dir` {}: {}", dir.display(), err));
        for file in read_dir {
            let file = file.unwrap();
            let path = file.path();
            if path.extension().unwrap_or_default() == "rs" {
                let rs = path;
                let txt = rs.with_extension("txt");
                let text = fs::read_to_string(&rs).unwrap();
                res.push(TestCase { rs, txt, text });
            }
        }
        res.sort();
        res
    }
}