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
127
use lazy_static::lazy_static;
use std::ops::RangeInclusive;

#[derive(Clone)]
pub struct CharFilter {
    table: [bool; 256],
}

impl CharFilter {
    pub fn new() -> CharFilter {
        CharFilter {
            table: [false; 256],
        }
    }

    pub fn add_char(&mut self, c: u8) -> () {
        self.table[c as usize] = true;
    }

    pub fn add_chars(&mut self, chars: RangeInclusive<u8>) -> () {
        for c in chars {
            self.table[c as usize] = true;
        }
    }

    pub fn add_chars_from_slice(&mut self, chars: &[u8]) -> () {
        for c in chars {
            self.table[*c as usize] = true;
        }
    }

    pub fn clone(&self) -> CharFilter {
        CharFilter {
            table: self.table.clone(),
        }
    }

    pub fn invert(&mut self) -> () {
        for i in 0..256 {
            self.table[i] = !self.table[i];
        }
    }

    pub fn has(&self, c: u8) -> bool {
        unsafe { *self.table.get_unchecked(c as usize) }
    }

    pub fn iter(&self) -> impl Iterator<Item = u8> + '_ {
        self.table
            .iter()
            .enumerate()
            .filter(|(_, e)| **e)
            .map(|(c, _)| c as u8)
    }
}

// WARNING: Does not consider Unicode characters allowed by spec.
pub const ID_START_CHARSTR: &'static [u8] =
    b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
pub const ID_CONTINUE_CHARSTR: &'static [u8] =
    b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";

lazy_static! {
    pub static ref DIGIT: CharFilter = {
        let mut filter = CharFilter::new();
        filter.add_chars(b'0'..=b'9');
        filter
    };

    pub static ref DIGIT_BIN: CharFilter = {
        let mut filter = CharFilter::new();
        filter.add_chars(b'0'..=b'1');
        filter
    };

    pub static ref DIGIT_HEX: CharFilter = {
        let mut filter = CharFilter::new();
        filter.add_chars(b'0'..=b'9');
        filter.add_chars(b'a'..=b'f');
        filter.add_chars(b'A'..=b'F');
        filter
    };

    pub static ref DIGIT_OCT: CharFilter = {
        let mut filter = CharFilter::new();
        filter.add_chars(b'0'..=b'8');
        filter
    };

    pub static ref ID_START: CharFilter = {
        let mut filter = CharFilter::new();
        filter.add_chars_from_slice(&ID_START_CHARSTR);
        filter
    };

    pub static ref ID_CONTINUE: CharFilter = {
        let mut filter = ID_START.clone();
        // WARNING: Does not consider Unicode characters allowed by spec.
        filter.add_chars(b'0'..=b'9');
        filter
    };

    pub static ref ID_CONTINUE_OR_PARENTHESIS_CLOSE_OR_BRACKET_CLOSE: CharFilter = {
        let mut filter = ID_CONTINUE.clone();
        filter.add_char(b')');
        filter.add_char(b']');
        filter
    };

    pub static ref WHITESPACE: CharFilter = {
        let mut filter = CharFilter::new();
        // WARNING: Does not consider Unicode whitespace allowed by spec.
        // Horizontal tab.
        filter.add_char(b'\x09');
        // Line feed.
        filter.add_char(b'\x0a');
        // Vertical tab.
        filter.add_char(b'\x0b');
        // Form feed.
        filter.add_char(b'\x0c');
        // Carriage return.
        filter.add_char(b'\x0d');
        // Space.
        filter.add_char(b'\x20');
        filter
    };
}