css_lexer/kindset.rs
1use crate::Kind;
2
3/// Match a token against one or more [Kinds][Kind].
4///
5/// Each [Kind] represents the token "type". [KindSet] is a bitmask of all possible [Kinds][Kind]. This is useful for
6/// efficiently comparing a token to see if it matches N token [Kinds][Kind].
7///
8/// # Example
9///
10/// ```
11/// use css_lexer::*;
12/// let mut lexer = Lexer::new("width: 1px");
13/// // The first token is either an AtKeyword, Ident or Function:
14/// assert_eq!(lexer.advance(), KindSet::new(&[Kind::AtKeyword, Kind::Ident, Kind::Function]));
15/// ```
16#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct KindSet(u32);
18
19impl KindSet {
20 /// A [KindSet] that matches no [Kinds][Kind].
21 pub const NONE: KindSet = KindSet::new(&[]);
22
23 /// A [KindSet] that matches all trivia; [Kind::Whitespace] and [Kind::Comment].
24 pub const TRIVIA: KindSet = KindSet::new(&[Kind::Whitespace, Kind::Comment]);
25
26 /// A [KindSet] that matches just Whitespace. This is the same as [Kind::Whitespace] but can be useful to apply to
27 /// functions that expect a [KindSet] rather than [Kind].
28 pub const WHITESPACE: KindSet = KindSet::new(&[Kind::Whitespace]);
29
30 /// A [KindSet] that matches just Whitespace. This is the same as [Kind::Comment] but can be useful to apply to
31 /// functions that expect a [KindSet] rather than [Kind].
32 pub const COMMENTS: KindSet = KindSet::new(&[Kind::Comment]);
33
34 /// A [KindSet] that matches either [Kind::RightCurly] or [Kind::Semicolon]. This is useful for matching
35 /// stop-tokens, for example checking the end of a declaration.
36 pub const RIGHT_CURLY_OR_SEMICOLON: KindSet = KindSet::new(&[Kind::RightCurly, Kind::Semicolon]);
37
38 /// A [KindSet] that matches either [Kind::LeftCurly] or [Kind::Semicolon]. This is useful for matching
39 /// stop-tokens, for example checking the end of an at-rule prelude.
40 pub const LEFT_CURLY_OR_SEMICOLON: KindSet = KindSet::new(&[Kind::LeftCurly, Kind::Semicolon]);
41
42 /// A [KindSet] that matches either [Kind::LeftCurly] or [Kind::RightParen] or [Kind::Semicolon]. This is useful for
43 /// matching stop-tokens, for example checking the end of a function.
44 pub const LEFT_CURLY_RIGHT_PAREN_OR_SEMICOLON: KindSet =
45 KindSet::new(&[Kind::LeftCurly, Kind::RightParen, Kind::Semicolon]);
46
47 /// A [KindSet] that matches either [Kind::LeftCurly] or [Kind::RightParen] or [Kind::Comma] or [Kind::Semicolon].
48 /// This is useful for matching stop-tokens, for example checking the end of a function or Selector.
49 pub const LEFT_CURLY_RIGHT_PAREN_COMMA_OR_SEMICOLON: KindSet =
50 KindSet::new(&[Kind::LeftCurly, Kind::RightParen, Kind::Comma, Kind::Semicolon]);
51
52 /// A [KindSet] that matches any single character token, such as [Kind::Delim] or [Kind::Colon] - [Kind::RightCurly].
53 pub const DELIM_LIKE: KindSet = KindSet::new(&[
54 Kind::Delim,
55 Kind::Colon,
56 Kind::Semicolon,
57 Kind::Comma,
58 Kind::LeftSquare,
59 Kind::RightSquare,
60 Kind::LeftParen,
61 Kind::RightParen,
62 Kind::LeftCurly,
63 Kind::RightCurly,
64 ]);
65
66 /// A [KindSet] that matches _any_ token.
67 pub const ANY: KindSet = KindSet(u32::MAX);
68
69 /// Creates a new [KindSet] with the combination of all given [Kinds][Kind].
70 ///
71 /// This function is marked `const` to allow creation of const [KindSets][KindSet].
72 pub const fn new(kinds: &[Kind]) -> Self {
73 let mut u = 0;
74 let mut i = 0;
75 let len = kinds.len();
76 while i < len {
77 u |= 1 << (kinds[i] as u8 % 32);
78 i += 1;
79 }
80 Self(u)
81 }
82
83 /// Returns a new [KindSet] with the addition of the supplied [Kind].
84 ///
85 /// This function is marked `const` to allow creation of const [KindSets][KindSet].
86 pub const fn add(&self, kind: Kind) -> Self {
87 Self(self.0 | (1 << (kind as u8 % 32)))
88 }
89
90 /// Check if a [KindSet] contains the subpplied [Kind].
91 pub fn contains(&self, kind: Kind) -> bool {
92 self.0 & (1 << (kind as u8 % 32)) != 0
93 }
94
95 pub(crate) const fn contains_bits(&self, kind_bits: u8) -> bool {
96 self.0 & (1 << (kind_bits % 32)) != 0
97 }
98}
99
100#[test]
101fn test_kindset_contains() {
102 let set = KindSet::new(&[Kind::Eof, Kind::Whitespace, Kind::Comment]);
103 assert!(set.contains(Kind::Eof));
104 assert!(set.contains(Kind::Whitespace));
105 assert!(set.contains(Kind::Comment));
106 assert!(!set.contains(Kind::String));
107 assert!(!set.contains(Kind::Url));
108}