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
128
129
130
131
132
133
134
use core::ops::RangeInclusive;
use lazy_static::lazy_static;

#[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: These do 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_JSX: CharFilter = {
        let mut filter = ID_CONTINUE.clone();
        filter.add_char(b'-');
        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
    };
}