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(&EmptyAtomSet::ATOMS, "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(u64);
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 just Whitespace or Semicolons.
35	pub const WHITESPACE_OR_SEMICOLON: KindSet = KindSet::new(&[Kind::Whitespace, Kind::Semicolon]);
36
37	/// A [KindSet] that matches either [Kind::RightCurly] or [Kind::Semicolon]. This is useful for matching
38	/// stop-tokens, for example checking the end of a declaration.
39	pub const RIGHT_CURLY_OR_SEMICOLON: KindSet = KindSet::new(&[Kind::RightCurly, Kind::Semicolon]);
40
41	/// A [KindSet] that matches either [Kind::LeftCurly] or [Kind::Semicolon]. This is useful for matching
42	/// stop-tokens, for example checking the end of an at-rule prelude.
43	pub const LEFT_CURLY_OR_SEMICOLON: KindSet = KindSet::new(&[Kind::LeftCurly, Kind::Semicolon]);
44
45	/// A [KindSet] that matches either [Kind::LeftCurly] or [Kind::RightParen] or [Kind::Semicolon]. This is useful for
46	/// matching stop-tokens, for example checking the end of a function.
47	pub const LEFT_CURLY_RIGHT_PAREN_OR_SEMICOLON: KindSet =
48		KindSet::new(&[Kind::LeftCurly, Kind::RightParen, Kind::Semicolon]);
49
50	/// A [KindSet] that matches either [Kind::LeftCurly] or [Kind::RightParen] or [Kind::Comma] or [Kind::Semicolon].
51	/// This is useful for matching stop-tokens, for example checking the end of a function or Selector.
52	pub const LEFT_CURLY_RIGHT_PAREN_COMMA_OR_SEMICOLON: KindSet =
53		KindSet::new(&[Kind::LeftCurly, Kind::RightParen, Kind::Comma, Kind::Semicolon]);
54
55	/// A [KindSet] that matches either [Kind::Ident], [Kind::AtKeyword], [Kind::Function], [Kind::Hash].
56	pub const IDENT_LIKE: KindSet = KindSet::new(&[Kind::Ident, Kind::AtKeyword, Kind::Function, Kind::Hash]);
57
58	/// A [KindSet] that matches any single character token, such as [Kind::Delim] or [Kind::Colon] - [Kind::RightCurly].
59	pub const DELIM_LIKE: KindSet = KindSet::new(&[
60		Kind::Delim,
61		Kind::Colon,
62		Kind::Semicolon,
63		Kind::Comma,
64		Kind::LeftSquare,
65		Kind::RightSquare,
66		Kind::LeftParen,
67		Kind::RightParen,
68		Kind::LeftCurly,
69		Kind::RightCurly,
70	]);
71
72	/// A [KindSet] that matches _any_ token.
73	pub const ANY: KindSet = KindSet(u64::MAX);
74
75	/// Creates a new [KindSet] with the combination of all given [Kinds][Kind].
76	///
77	/// This function is marked `const` to allow creation of const [KindSets][KindSet].
78	pub const fn new(kinds: &[Kind]) -> Self {
79		let mut u = 0;
80		let mut i = 0;
81		let len = kinds.len();
82		while i < len {
83			u |= 1 << (kinds[i] as u8 & 0b111111);
84			i += 1;
85		}
86		Self(u)
87	}
88
89	/// Returns a new [KindSet] with the addition of the supplied [Kind].
90	///
91	/// This function is marked `const` to allow creation of const [KindSets][KindSet].
92	pub const fn add(&self, kind: Kind) -> Self {
93		Self(self.0 | (1 << (kind as u8 & 0b111111)))
94	}
95
96	/// Check if a [KindSet] contains the subpplied [Kind].
97	pub fn contains(&self, kind: Kind) -> bool {
98		self.0 & (1 << (kind as u8 & 0b111111)) != 0
99	}
100
101	pub(crate) const fn contains_bits(&self, kind_bits: u8) -> bool {
102		self.0 & (1 << (kind_bits & 0b111111)) != 0
103	}
104}
105
106#[test]
107fn test_kindset_contains() {
108	let set = KindSet::new(&[Kind::Eof, Kind::Whitespace, Kind::Comment]);
109	assert!(set.contains(Kind::Eof));
110	assert!(set.contains(Kind::Whitespace));
111	assert!(set.contains(Kind::Comment));
112	assert!(!set.contains(Kind::String));
113	assert!(!set.contains(Kind::Url));
114
115	let set = KindSet::new(&[Kind::LeftCurly, Kind::LeftSquare, Kind::LeftParen]);
116
117	assert!(set.contains(Kind::LeftCurly));
118	assert!(!set.contains(Kind::RightCurly));
119	assert!(set.contains(Kind::LeftSquare));
120	assert!(!set.contains(Kind::RightSquare));
121	assert!(set.contains(Kind::LeftParen));
122	assert!(!set.contains(Kind::RightParen));
123	assert!(!set.contains(Kind::Ident));
124
125	assert!(KindSet::COMMENTS.contains(Kind::Comment));
126	assert!(!KindSet::COMMENTS.contains(Kind::Delim));
127}