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
/// Returns whether `chr` is a valid atom character outside a
/// string (i.e. one of `:atomchar:` documented at `TreeNode::Atom`).
#[inline]
pub const fn is_atom_chr(chr: char) -> bool {
    matches!(
        chr,
        '!' | '#'
            | '$'
            | '%'
            | '&'
            | '*'
            | '+'
            | '-'
            | '.'
            | '/'
            | '0'..='9'
            | ':'
            | '<'
            | '='
            | '>'
            | '?'
            | '@'
            | 'A'..='Z'
            | '_'
            | 'a'..='z'
            | '~'
    )
}

/// Returns whether `chr` is a valid atom character inside a
/// string, excluding `"` and `\` (i.e. one of `:stringchar:`
/// documented at `TreeNode::Atom`).
#[inline]
pub const fn is_atom_string_chr(chr: char) -> bool {
    matches!(chr, ' '..='~' if chr != '"' && chr != '\\')
}

/// Checks whether `atom` is a valid atom (i.e. matches the regular
/// expression documented at `TreeNode::Atom`).
pub fn check_atom(atom: &str) -> bool {
    if atom.is_empty() {
        // Empty atom
        return false;
    }

    let mut iter = atom.chars();
    let mut in_string = false;
    loop {
        if !in_string {
            match iter.next() {
                None => return true,
                Some('"') => in_string = true,
                Some(chr) if is_atom_chr(chr) => {}
                // Invalid character
                Some(_) => return false,
            }
        } else {
            match iter.next() {
                // Unfinished string
                None => return false,
                Some('"') => in_string = false,
                Some('\\') => match iter.next() {
                    Some('"' | '\\') => {}
                    Some(chr) if is_atom_string_chr(chr) => {}
                    // Invalid character or unfinished string
                    Some(_) | None => return false,
                },
                Some(chr) if is_atom_string_chr(chr) => {}
                // Invalid character
                Some(_) => return false,
            }
        }
    }
}