Skip to main content

css_lexer/
token.rs

1use crate::{
2	AssociatedWhitespaceRules, CommentStyle, Cursor, Kind, KindSet, PairWise, QuoteStyle, SourceOffset, Whitespace,
3	constants::SINGLE_CHAR_KINDS,
4};
5use source_tools::SourceToken as SourceTokenTrait;
6use std::char::REPLACEMENT_CHARACTER;
7
8/// An abstract representation of the chunk of the source text, retaining certain "facts" about the source.
9///
10/// # Design
11///
12/// The [Token] type is an immutable packing of two [u32s][u32] that represents a unit in the source text, but without
13/// the associated offset data that points to its position in the source text. This is important because it means that
14/// equivalent [Tokens][Token] are equal even in different parts of the document. For the most part a [Token] doesn't
15/// represent data that can be put into a text file because it lacks the underlying character data. It is lossy. For
16/// example a [Token] with [Kind::Ident] just represents _an_ ident, but it doesn't retain what the keyword is).
17/// Storing raw-character data would require either storing tokens on the heap (and therefore they couldn't be [Sized])
18/// or by keeping a reference to `&'a str` which means larger token sizes and lifetime tracking. By _not_ storing
19/// character data we can keep [Token] [Sized] and keep it to `size_of` `8`, avoiding the heap, avoiding
20/// references/lifetimes, and keeping [Token] entirely in the stack. For a lot of tokens this is _fine_ because the
21/// underlying character data isn't that useful past a certain point.
22///
23/// A [Token] retains certain "facts" about the underlying unit of text, though. For example it retains the [Kind], how
24/// many characters the token consumed, and various other pieces of information, depending on the [Kind]. In some
25/// cases, it's entirely possible to represent the full token, including character data, into the available bits (for
26/// example [Kind::Delim] stores its [char], [Kind::Number] stores its [f32]). Taking the time in the tokenizer to
27/// gather these facts and values can keep cache-lines hot, which speeds up subsequent checks in the parser.
28///
29/// If you're familiar with "red green" syntax trees such as [Swiftlang's libsyntax][1], or [Rust-Analyzer's Rowan][2]
30/// or [Roslyn][3] this might be a little familiar in some concepts. However [Token] does not represent a tree, and
31/// relies on resorting back to the string data to find out keyword values.
32///
33/// [1]: https://gh.io/AAtdqpg
34/// [2]: https://gh.io/AAtf8pt
35/// [3]: https://gh.io/AAtab90
36///
37/// This representation of facts, kind, length, or other metadata can be quite complex - so here's a
38/// full breakdown:
39///
40/// # Anatomy of Token
41///
42/// A [Token] is a struct of `(u32, u32)`. The second u32 is _usually_ the token length (hence keeping them separate).
43/// The first [u32], however, is split into 3 (sometimes 5) parts. The two u32s can be thought of like so:
44///
45/// ```md
46///   |------|------|--------------------------|---------------------------------|
47///   | TF   | K    | VD                       | Value                           |
48/// 0b| 0000 | 0000 | 000000000000000000000000 | 0000000000000000000000000000000 |
49///   |------|------|--------------------------|---------------------------------|
50///   | 4--- | 4--- | 24---------------------- | 32----------------------------- |
51/// ```
52///
53/// ## TF = Type Flags (or "Token Facts")
54///
55/// This represents a bit-mask in the upper-most 3 bits. The flags are general purpose and change meaning depending on
56/// the Token's [Kind]. Each flag generally maps to a method so it's not necessary to remenber the contents of this
57/// table, but it can serve as a useful reference. Note that not all methods return a [bool], so footnotes have been
58/// added to explain these further.
59///
60/// | Kind::              | Flag   | Description                 | Method                                   |
61/// |---------------------|--------|-----------------------------|------------------------------------------|
62/// | [Kind::Number]      | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
63/// |                     | `0010` | Floating Point              | [Token::is_float()]                      |
64/// |                     | `0100` | Has a "Sign" (-/+)          | [Token::has_sign()]                      |
65/// |                     | `1000` | Sign is required            | [Token::sign_is_required()]              |
66/// | [Kind::Dimension]   | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
67/// |                     | `0010` | Floating Point              | [Token::is_float()]                      |
68/// |                     | `0100` | Has a "Sign" (-/+)          | [Token::has_sign()]                      |
69/// |                     | `1000` | Unit is a known dimension   | [Token::atom_bits()][^dimension]         |
70/// | [Kind::String]      | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
71/// |                     | `0010` | Uses Double Quotes          | [Token::quote_style()][^quotes]          |
72/// |                     | `0100` | Has a closing quote         | [Token::has_close_quote()]               |
73/// |                     | `1000` | Contains escape characters  | [Token::contains_escape_chars()]         |
74/// | [Kind::Ident]       | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
75/// |                     | `0010` | Contains non-lower-ASCII    | [Token::is_lower_case()]                 |
76/// |                     | `0100` | Is a "Dashed Ident"         | [Token::is_dashed_ident()]               |
77/// |                     | `1000` | Contains escape characters  | [Token::contains_escape_chars()]         |
78/// | [Kind::Function]    | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
79/// |                     | `0010` | Contains non-lower-ASCII    | [Token::is_lower_case()]                 |
80/// |                     | `0100` | Is a "Dashed Ident"         | [Token::is_dashed_ident()]               |
81/// |                     | `1000` | Contains escape characters  | [Token::contains_escape_chars()]         |
82/// | [Kind::AtKeyword]   | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
83/// |                     | `0010` | Contains non-lower-ASCII    | [Token::is_lower_case()]                 |
84/// |                     | `0100` | Is a "Dashed Ident"         | [Token::is_dashed_ident()]               |
85/// |                     | `1000` | Contains escape characters  | [Token::contains_escape_chars()]         |
86/// | [Kind::Hash]        | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
87/// |                     | `0010` | Contains non-lower-ASCII    | [Token::is_lower_case()]                 |
88/// |                     | `0100` | First character is ASCII    | [Token::hash_is_id_like()]               |
89/// |                     | `1000` | Contains escape characters  | [Token::contains_escape_chars()]         |
90/// | [Kind::Url]         | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
91/// |                     | `0010` | Has a closing paren )       | [Token::url_has_closing_paren()]         |
92/// |                     | `0100` | Contains whitespace after ( | [Token::url_has_leading_space()]         |
93/// |                     | `1000` | Contains escape characters  | [Token::contains_escape_chars()]         |
94/// | [Kind::UnicodeRange]| `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
95/// |                     | `0010` | (Reserved)                  | --                                       |
96/// |                     | `0100` | (Reserved)                  | --                                       |
97/// |                     | `1000` | (Reserved)                  | --                                       |
98/// | [Kind::CdcOrCdo]    | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
99/// |                     | `0010` | Is CDO (`000` would be CDC) | [Token::is_cdc()]                        |
100/// |                     | `0100` | (Reserved)                  | --                                       |
101/// |                     | `1000` | (Reserved)                  | --                                       |
102/// | [Kind::Whitespace]  | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
103/// |                     | `???0` | Whitespace style            | [Token::whitespace_style()][^whitespace] |
104/// | [Kind::Delim]       | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
105/// |                     | `???0` | Associate whitespace rules  | [Token::associated_whitespace()][^delim] |
106/// | [Kind::Comment]     | `0001` | Error/Recovery token        | [Token::is_bad()][^bad]                  |
107/// |                     | `???0` | (Special)                   | [Token::comment_style()][^comments]      |
108///
109/// [^bad]: All tokens use the 4th bit to denote if this token is a "Erorr/Recovery Token". These tokens are not going
110/// to be emitted by the lexer (except in the case of BadString & BadUrl), but a Parser can set this flag on a token to
111/// help differentiate between tokens emitted by the lexer and tokens that were either emitted by the lexer but in an
112/// unexpected position, or tokens _constructed_ by the parser in order to aid in recovering the Parser into a state to
113/// resume.
114/// [^quotes]: Strings do not have a [bool] returning method for whether or not the quote is using double or single
115/// quotes, instead the [Token::quote_style()] method will returning the [QuoteStyle] enum for better readability.
116/// [^whitespace]: Whitespace tokens to not have a [bool] returning method, instead [Token::whitespace_style()] will
117/// return the [Whitespace] enum for improved readability.
118/// [^comments]: Rather than using the 3 bits as a bit-mask, Comment tokens use the data to store the [CommentStyle]
119/// enum, which is capable of representing 8 discrete comment styles.
120/// [^delim]: Delims can be used in interesting ways inside of CSS syntax. At higher levels CSS is _sometimes_
121/// whitespace sensitive, for example the whitespace inside of a CSS selector _sometimes_ represents the descendant
122/// combinator, meanwhile delimiters inside calc() are sensitive to whitespace collapse (`calc(1px + 1px)` is valid
123/// while `calc(1px+1px)` is a parse error). Further to this, introducing whitespace (say through a formatter) might
124/// break in interesting ways due to some combinations of Delims & Idents - for example Pseudo Classes like `:hover`,
125/// or CSS like languages such as SASS using `$var` style syntax. While `:hover` and `$var` are comprised of two tokens
126/// they're considered one conceptual unit. Having a way to express these relationships at the token level can be useful
127/// for other low level machinery such as formatters/minifiers, rather than introducing complex state at higher levels.
128/// For these reasons, Delim tokens have the ability to express their whitespace association. The lexer will always
129/// produce a token with empty whitespace rules, but parsers can replace this token with a more complex set of rules.
130///
131/// ## K = Kind Bits
132///
133/// The `K` value - upper-most bits 4-9 stores the 5-bit [Kind].
134///
135/// ## VD = Value Data
136///
137/// The `VD` value - the lower-most 24-bits - stores data depending on the [Token] [Kind]. For most kinds this data is
138/// reserved (just 0s). The value data cannot be interrogated manually, but it packs in additional data about the
139/// underlying string to make the string easier to parse without doing the same lookups that the tokenizer already had
140/// to - such as determining lengths of the various parts of the token, or packing values so that consulting the string
141/// can be avoided (which keeps cache-lines hot).
142///
143/// Below describes the special kinds which use the Value Data to store yet more information about the token...
144///
145/// ### Value Data for [Kind::Ident], [Kind::Function], [Kind::AtKeyword]
146///
147/// If the [Kind] is [Kind::Ident], [Kind::Function], or [Kind::AtKeyword] then Value Data represents the Ident's "Atom
148/// Data". When lexing one of these tokens the Lexer will pass the string slice to [DynAtomSet][atom_set::DynAtomSet]
149/// and set this bits accordingly. This allows implementations to provide a [DynAtomSet][atom_set::DynAtomSet] of
150/// interned strings to improve performance of string comparisons. The `ATOM_DYNAMIC_BIT` can be used to dynamically
151/// intern strings during runtime (this behaviour is abstracted by [DynAtomRegistry][crate::DynAtomRegistry]). This
152/// 24-bits allows for ~16MM unique strings, but with the `ATOM_DYNAMIC_BIT` this becomes ~8MM static atoms and ~8MM
153/// dynamic atoms (very unlikely CSS will ever reach even 10k predefined keywords, and most CSS files will have less
154/// than 1000 unique strings).
155///
156/// ### Value Data for [Kind::Number]
157///
158/// If the [Kind] is [Kind::Number], Value Data represents the length of that number (this means the parser is
159/// restricted from representing numbers longer than 16,777,216 characters which is probably an acceptable limit). Note
160/// that this does not affect the _value_ of a number, just the characters in a string. Numbers in CSS are [f32]. The
161/// vast majority of [f32s][f32] can be represented in 16MM characters, but it's possible to author a document that
162/// contains a set of numeric characters longer than 16MM code points. These scenarios are considered [undefined
163/// behaviour][1].
164///
165/// [4]: https://en.wikipedia.org/wiki/Undefined_behavior
166///
167/// ### Value Data for [Kind::Hash]
168///
169/// If the [Kind] is [Kind::Hash], Value Data represents the length of that hash (this means the parser is restricted
170/// from representing IDs and hex codes longer than 16,777,216 characters which is probably an acceptable limit). Note
171/// that this restriction means that ID selectors have a much tigher limit than other tokens, such as strings or
172/// idents, but it's very unlikely to see a 16million character ID in CSS (String, maybe).
173///
174/// ### Value Data for [Kind::Delim] and single character tokens
175///
176/// If the [Kind] is [Kind::Delim] (which includes the single character tokens [Kind::Colon]->[Kind::RightCurly]),
177/// Value Data stores the [Kind] the [char] resolves to, XORed with [Kind::Delim]. A plain [Kind::Delim] has empty Value
178/// Data, so [Token::kind()] resolves single character tokens without looking at the [char].
179///
180/// ### Value Data for [Kind::Url]
181///
182/// If the [Kind] is [Kind::Url], Value Data represents the "leading length" and "trailing length" of the URL. This
183/// means the value data is split into two 12 bit numbers:
184///
185/// ```md
186/// |--------------|--------------|
187/// | LL           | TL           |
188/// | 000000000000 | 000000000000 |
189/// |--------------|--------------|
190/// | 12---------- | 12---------- |
191/// ```
192///
193/// The "leading" length represents the `url(` part of the token. Typically this will be `4`, however it's possible
194/// (for legacy compatibility reasons within CSS) to add whitespace between the opening parenthesis and the URL value.
195/// It's also possible to escape the `url` ident portion. This means `\75\52\6c(   ` is also a valid leading section of
196/// a URL ident (which has a character length of 13), as is `\000075 \000052 \00006c (   ` (28 characters). 12 bits
197/// allows for a maximum character length of 4,096. It is not possible to represent a URL token's leading section using
198/// 4,096 characters so there is some headroom (wasted bytes) here.
199///
200/// The "trailing" length represents the `)` part of the token. Typically this will be `1`, however it's possible to
201/// add any number of whitespace characters between the end of the URL and the closing parenthesis. If a CSS document
202/// contains more than 4095 whitespace characters then this is considered [undefined behaviour][4].
203///
204/// ### Value Data for [Kind::Dimension]
205///
206/// If K is a Dimension, then this represents both the number of characters in the numeric portion of the dimension
207/// and the length of the ident portion of the dimension... or the dimension unit itself (more on that below). This
208/// means the value data is split into two 12 bit numbers:
209///
210/// ```md
211/// |--------------|--------------|
212/// | NL           | DUL          |
213/// | 000000000000 | 000000000000 |
214/// |--------------|--------------|
215/// | 12---------- | 12---------- |
216///
217/// |--------------|-------| --------|
218/// | NL           | KDUL  | KNOWN   |
219/// | 000000000000 | 00000 | 0000000 |
220/// |--------------|-------| --------|
221/// | 12---------- | 5---- | 7------ |
222/// ```
223///
224/// The NL portion - the numeric length - represents the length of characters the number contains. This means the
225/// numeric portion of a dimension can only be 4,096 characters long. This is dramatically shorter than the 16MM
226/// allowed for numbers but it's still also incredibly generous such that it's highly unlikely to ever be hit unless
227/// someone is intentionally trying to break the parser. The [Lexer][super::Lexer] encountering a dimension with a
228/// numeric portion longer than 4,096 characters is considered [undefined behaviour][4].
229///
230/// The DUL portion (if `TF & 100 == 0`) will represent the length of characters the ident portion of the dimension
231/// (aka the dimension unit) contains. This means the ident portion of a dimension can only be 4,096 characters long.
232/// For practical purposes CSS has a fixed set of dimensions - the longest of which (at the time of writing) are 5
233/// characters long (e.g. `svmax`). Through the use of escaping shenanigans it's possible to create a valid CSS
234/// dimension longer than 5 characters though (every ident can be made 8 times longer by using escape characters, e.g.
235/// `1svmax` at 6 characters can be instead written as `1\000073 \000076 \00006d \000061 \000078` at 40 characters). In
236/// addition to these factors, it's worth pointing out that there is scope for further dimensions and some [proposals
237/// for "custom" dimensions][5], and lastly this library is designed for CSS _and CSS-alike_ languages, which may
238/// invent their own dimension units. In other words being too restrictive on dimension ident length could be costly
239/// in the future, therefore 4,096 characters seems like a reasonable, if generous, trade-off.
240///
241/// There's a giant caveat here though. If `TF & 1000 != 0`, then the dimension is considered "known" and DUL will be
242/// encoded differently. Instead of just containing the dimension unit length, which requires consulting the underlying
243/// `&str` to get the actual dimension, it will be used to store an Atom - but only the first 7 bits (the KNOWN
244/// portion), which for an Atom must be a Dimension atom (an assummption made on anything that implements
245/// [AtomSet][atom_set::AtomSet] is that all dimension units should be stored in the byte values of 1-127, so that they
246/// can be encoded in this space). Dimension units _can_ be escape encoded, and so the underlying character data may
247/// differ from the unescaped unit length, as such 5-bit KDUL portion represents character data length, in other words
248/// `KNOWN.len()` may not always equal KDUL`.
249///
250/// [5]: https://github.com/w3c/csswg-drafts/issues/7379
251///
252/// ## Value
253///
254/// The `Value` portion of [Token] represents the length of the token for most token kinds. However, for some tokens
255/// their length is already packed into the first u32. So it would make more sense to use this u32 to store more
256/// interesting data.
257///
258/// ## Value for [Kind::Delim] and single character tokens
259///
260/// [Kind::Delim] and single-character tokens (i.e. [Kind::Colon]->[Kind::RightCurly]) typically have a length of `1`
261/// ([Kind::Delim] can have a varied length for surrogate pairs). Instead of storing the length and wasting a whole
262/// [u32], this region stores the [char]. Calling [Token::char()] will return an [Option] which will always be [Some]
263/// for [Kind::Delim] and single-character tokens.
264///
265/// ## Value for [Kind::Hash]
266///
267/// The length of a hash is stored in its `VD` portion, leaving 32bits to storing other data. It just so happens that
268/// a 8-character hex code (#ffaabbcc) fits nicely inside of 32-bits. During tokenization we can eagerly parse the hex
269/// code and stuff it here, so it can be more easily reasoned about in upstream code (rather than
270/// reading the character data).
271///
272/// ## Value for [Kind::Number] and [Kind::Dimension]
273///
274/// As these tokens store their length data in the `VD` portion, this [u32] instead stores the _value_ of the number,
275/// stored as [f32::to_bits()].
276///
277/// ## Value data for other tokens.
278///
279/// In all other cases, this represents the length of the token as utf-8 bytes. This means the token length is
280/// 4,294,967,296 aka ~4GB. This sounds very long but also CSS can host very large image data and browsers will
281/// accomodate very large URLs. [An mdn article on Data URLs][6] claims that Firefox supports 32mb Data URLs, Chrome
282/// supports over 512mb, and Safari over 2gb. The reality is that if someone has such a large data URL in their CSS
283/// they probably should split it out, but we have a whole 32 bits to store the length so we may as well use it...
284///
285/// [6]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs#common_problems
286#[repr(C)]
287#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
288pub struct Token(u32, u32);
289
290impl Default for Token {
291	fn default() -> Self {
292		Self((Kind::Whitespace as u32) << 24, 0)
293	}
294}
295
296const KIND_MASK: u32 = !((1 << 24) - 1);
297const LENGTH_MASK: u32 = (1 << 24) - 1;
298const HALF_LENGTH_MASK: u32 = !((1 << 12) - 1);
299
300/// The bit position used to distinguish between static and dynamic atoms.
301/// - Static atoms have this bit = 0 (values 0 to 8,388,607)
302/// - Dynamic atoms have this bit = 1 (values 8,388,608 to 16,777,215)
303///
304/// This allows atoms to use the full 24-bit space available in token data.
305#[allow(dead_code)] // Used in dyn_atom_registry module
306pub(crate) const ATOM_DYNAMIC_BIT: u32 = 23;
307
308impl Token {
309	/// Represents an empty token.
310	pub const EMPTY: Token = Token::new_whitespace(Whitespace::none(), 0);
311
312	/// Represents an EOF token.
313	pub const EOF: Token = Token(0b0, 0);
314
315	/// Represents a CDO (`<!--`) token.
316	pub const CDO: Token = Token(((Kind::CdcOrCdo as u32) << 24) & KIND_MASK, 4);
317
318	/// Represents a CDC (`-->`) token.
319	pub const CDC: Token = Token((((Kind::CdcOrCdo as u32) | 0b001_00000) << 24) & KIND_MASK, 3);
320
321	/// Represents a single ' ' space token.
322	pub const SPACE: Token = Token::new_whitespace(Whitespace::Space, 1);
323
324	/// Represents a single Tab token.
325	pub const TAB: Token = Token::new_whitespace(Whitespace::Tab, 1);
326
327	/// Represents a single `\n` token.
328	pub const NEWLINE: Token = Token::new_whitespace(Whitespace::Newline, 1);
329
330	/// Represents the Number `0`. This is not equal to other representations of zero, such as `00`, `0e0`, `0.0` and so
331	/// on.
332	pub const NUMBER_ZERO: Token = Token((((Kind::Number as u32) | 0b100_00000) << 24) & KIND_MASK, 1);
333
334	/// Represents the `:` token.
335	pub const COLON: Token = Token::new_delim(':');
336
337	/// Represents the `;` token.
338	pub const SEMICOLON: Token = Token::new_delim(';');
339
340	/// Represents the `,` token.
341	pub const COMMA: Token = Token::new_delim(',');
342
343	/// Represents the `[` token.
344	pub const LEFT_SQUARE: Token = Token::new_delim('[');
345
346	/// Represents the `]` token.
347	pub const RIGHT_SQUARE: Token = Token::new_delim(']');
348
349	/// Represents the `(` token.
350	pub const LEFT_PAREN: Token = Token::new_delim('(');
351
352	/// Represents the `)` token.
353	pub const RIGHT_PAREN: Token = Token::new_delim(')');
354
355	/// Represents the `{` token.
356	pub const LEFT_CURLY: Token = Token::new_delim('{');
357
358	/// Represents the `}` token.
359	pub const RIGHT_CURLY: Token = Token::new_delim('}');
360
361	/// Represents a `!` [Kind::Delim] token.
362	pub const BANG: Token = Token::new_delim('!');
363
364	/// Represents a `#` [Kind::Delim] token.
365	pub const HASH: Token = Token::new_delim('#');
366
367	/// Represents a `$` [Kind::Delim] token.
368	pub const DOLLAR: Token = Token::new_delim('$');
369
370	/// Represents a `%` [Kind::Delim] token - not to be confused with the `%` dimension.
371	pub const PERCENT: Token = Token::new_delim('%');
372
373	/// Represents a `&` [Kind::Delim] token.
374	pub const AMPERSAND: Token = Token::new_delim('&');
375
376	/// Represents a `*` [Kind::Delim] token.
377	pub const ASTERISK: Token = Token::new_delim('*');
378
379	/// Represents a `+` [Kind::Delim] token.
380	pub const PLUS: Token = Token::new_delim('+');
381
382	/// Represents a `-` [Kind::Delim] token.
383	pub const DASH: Token = Token::new_delim('-');
384
385	/// Represents a `.` [Kind::Delim] token.
386	pub const PERIOD: Token = Token::new_delim('.');
387
388	/// Represents a `/` [Kind::Delim] token.
389	pub const SLASH: Token = Token::new_delim('/');
390
391	/// Represents a `<` [Kind::Delim] token.
392	pub const LESS_THAN: Token = Token::new_delim('<');
393
394	/// Represents a `=` [Kind::Delim] token.
395	pub const EQUALS: Token = Token::new_delim('=');
396
397	/// Represents a `>` [Kind::Delim] token.
398	pub const GREATER_THAN: Token = Token::new_delim('>');
399
400	/// Represents a `?` [Kind::Delim] token.
401	pub const QUESTION: Token = Token::new_delim('?');
402
403	/// Represents a `@` [Kind::Delim] token. Not to be confused with the @keyword token.
404	pub const AT: Token = Token::new_delim('@');
405
406	/// Represents a `\\` [Kind::Delim] token.
407	pub const BACKSLASH: Token = Token::new_delim('\\');
408
409	/// Represents a `^` [Kind::Delim] token.
410	pub const CARET: Token = Token::new_delim('^');
411
412	/// Represents a `_` [Kind::Delim] token.
413	pub const UNDERSCORE: Token = Token::new_delim('_');
414
415	/// Represents a `\`` [Kind::Delim] token.
416	pub const BACKTICK: Token = Token::new_delim('\'');
417
418	/// Represents a `|` [Kind::Delim] token.
419	pub const PIPE: Token = Token::new_delim('|');
420
421	/// Represents a `~` [Kind::Delim] token.
422	pub const TILDE: Token = Token::new_delim('~');
423
424	/// Represents a replacement character [Kind::Delim] token.
425	pub const REPLACEMENT_CHARACTER: Token = Token::new_delim(REPLACEMENT_CHARACTER);
426
427	/// Creates a "Dummy" token with no additional data, just the [Kind].
428	#[inline]
429	pub const fn dummy(kind: Kind) -> Self {
430		let char = match kind {
431			Kind::Colon => ':',
432			Kind::Semicolon => ';',
433			Kind::Comma => ',',
434			Kind::LeftSquare => '[',
435			Kind::RightSquare => ']',
436			Kind::LeftParen => '(',
437			Kind::RightParen => ')',
438			Kind::LeftCurly => '{',
439			Kind::RightCurly => '}',
440			_ => return Self((kind as u32) << 24, 0).with_bad_flag(),
441		};
442		Self::new_delim(char).with_bad_flag()
443	}
444
445	/// Creates a "Dummy" token with no additional data, just [Kind::Ident].
446	#[inline]
447	pub const fn dummy_ident() -> Self {
448		Self((Kind::Ident as u32) << 24, 0).with_bad_flag()
449	}
450
451	/// Creates a [Kind::Whitesapce] token.
452	#[inline]
453	pub(crate) const fn new_whitespace(style: Whitespace, len: u32) -> Self {
454		let flags: u32 = Kind::Whitespace as u32 | ((style.to_bits() as u32) << 5);
455		Self((flags << 24) & KIND_MASK, len)
456	}
457
458	/// Creates a [Kind::Comment] token.
459	#[inline]
460	pub(crate) const fn new_comment(style: CommentStyle, len: u32) -> Self {
461		let flags: u32 = Kind::Comment as u32 | ((style as u32) << 5);
462		Self((flags << 24) & KIND_MASK, len)
463	}
464
465	/// Creates a [Kind::Number] token.
466	#[inline]
467	pub(crate) fn new_number(is_float: bool, has_sign: bool, len: u32, value: f32) -> Self {
468		let flags: u32 = Kind::Number as u32 | ((is_float as u32) << 5) | ((has_sign as u32) << 6);
469		Self((flags << 24) & KIND_MASK | (len & LENGTH_MASK), value.to_bits())
470	}
471
472	/// Creates a new [Kind::Dimension] token.
473	#[inline]
474	pub(crate) fn new_dimension(
475		is_float: bool,
476		has_sign: bool,
477		num_len: u32,
478		unit_len: u32,
479		value: f32,
480		atom: u8,
481	) -> Self {
482		debug_assert!(num_len <= 4097);
483		let num_len = (num_len << 12) & HALF_LENGTH_MASK;
484		let is_known_unit = if unit_len < 32 { ((atom != 0) as u32) << 7 } else { 0 };
485		let unit_len = if is_known_unit == 0 { unit_len } else { unit_len << 7 | (atom as u32 & 0b1111111) };
486		let flags: u32 = Kind::Dimension as u32 | is_known_unit | ((is_float as u32) << 5) | ((has_sign as u32) << 6);
487		Self(((flags << 24) & KIND_MASK) | ((num_len | unit_len) & LENGTH_MASK), value.to_bits())
488	}
489
490	/// Creates a new [Kind::BadString] token. Bad Strings are like String tokens but during lexing they failed to fully tokenize
491	/// into a proper string token, usually due to containing newline characters.
492	#[inline]
493	pub(crate) fn new_bad_string(len: u32) -> Self {
494		Self(((Kind::BadString as u32) << 24) & KIND_MASK, len)
495	}
496
497	/// Creates a new [Kind::BadUrl] token. Bad URLs are like URL tokens but during lexing they failed to fully tokenize into a
498	/// proper URL token, usually due to containing newline characters.
499	#[inline]
500	pub(crate) fn new_bad_url(len: u32) -> Self {
501		Self(((Kind::BadUrl as u32) << 24) & KIND_MASK, len)
502	}
503
504	/// Creates a new [Kind::Ident] token.
505	#[inline]
506	pub(crate) fn new_ident(
507		contains_non_lower_ascii: bool,
508		dashed: bool,
509		contains_escape: bool,
510		atom: u32,
511		len: u32,
512	) -> Self {
513		let flags: u32 = Kind::Ident as u32
514			| ((contains_non_lower_ascii as u32) << 5)
515			| ((dashed as u32) << 6)
516			| ((contains_escape as u32) << 7);
517		debug_assert!(atom & LENGTH_MASK == atom);
518		Self((flags << 24) & KIND_MASK | atom, len)
519	}
520
521	/// Creates a new [Kind::Function] token.
522	#[inline]
523	pub(crate) fn new_function(
524		contains_non_lower_ascii: bool,
525		dashed: bool,
526		contains_escape: bool,
527		atom: u32,
528		len: u32,
529	) -> Self {
530		let flags: u32 = Kind::Function as u32
531			| ((contains_non_lower_ascii as u32) << 5)
532			| ((dashed as u32) << 6)
533			| ((contains_escape as u32) << 7);
534		debug_assert!(atom & LENGTH_MASK == atom);
535		Self((flags << 24) & KIND_MASK | atom, len)
536	}
537
538	/// Creates a new [Kind::AtKeyword] token.
539	#[inline]
540	pub(crate) fn new_atkeyword(
541		contains_non_lower_ascii: bool,
542		dashed: bool,
543		contains_escape: bool,
544		atom: u32,
545		len: u32,
546	) -> Self {
547		let flags: u32 = Kind::AtKeyword as u32
548			| ((contains_non_lower_ascii as u32) << 5)
549			| ((dashed as u32) << 6)
550			| ((contains_escape as u32) << 7);
551		debug_assert!(atom & LENGTH_MASK == atom);
552		Self((flags << 24) & KIND_MASK | atom, len)
553	}
554
555	/// Creates a new [Kind::Hash] token.
556	#[inline]
557	pub(crate) fn new_hash(
558		contains_non_lower_ascii: bool,
559		first_is_ascii: bool,
560		contains_escape: bool,
561		len: u32,
562		hex_value: u32,
563	) -> Self {
564		let flags: u32 = Kind::Hash as u32
565			| ((contains_non_lower_ascii as u32) << 5)
566			| ((first_is_ascii as u32) << 6)
567			| ((contains_escape as u32) << 7);
568		debug_assert!(len < (1 << 24));
569		Self((flags << 24) & KIND_MASK | (len & LENGTH_MASK), hex_value)
570	}
571
572	/// Creates a new [Kind::String] token.
573	#[inline]
574	pub(crate) fn new_string(quotes: QuoteStyle, has_close_quote: bool, contains_escape: bool, len: u32) -> Self {
575		debug_assert!(quotes != QuoteStyle::None);
576		let quotes = if quotes == QuoteStyle::Double { 0b001_00000 } else { 0b0 };
577		let flags: u32 =
578			Kind::String as u32 | quotes | ((has_close_quote as u32) << 6) | ((contains_escape as u32) << 7);
579		Self((flags << 24) & KIND_MASK, len)
580	}
581
582	/// Creates a new [Kind::Url] token.
583	#[inline]
584	pub(crate) fn new_url(
585		ends_with_paren: bool,
586		contains_whitespace_after_open_paren: bool,
587		contains_escape: bool,
588		leading_length: u32,
589		trailing_length: u32,
590		len: u32,
591	) -> Self {
592		let leading_length = (leading_length << 12) & HALF_LENGTH_MASK;
593		let flags: u32 = Kind::Url as u32
594			| ((ends_with_paren as u32) << 5)
595			| ((contains_whitespace_after_open_paren as u32) << 6)
596			| ((contains_escape as u32) << 7);
597		Self((flags << 24) & KIND_MASK | ((leading_length | trailing_length) & LENGTH_MASK), len)
598	}
599
600	/// Creates a new [Kind::UnicodeRange] token.
601	#[inline]
602	pub(crate) fn new_unicode_range(start: u32, end: u32, len: u32) -> Self {
603		debug_assert!(start <= 0xFFFFFF);
604		debug_assert!(end <= 0xFFFFFF);
605		debug_assert!(len <= 255);
606		let flags: u32 = Kind::UnicodeRange as u32;
607		Self((flags << 24) & KIND_MASK | (start & LENGTH_MASK), (len << 24) | (end & LENGTH_MASK))
608	}
609
610	/// If the [Token] is [Kind::UnicodeRange], returns the start value of the range.
611	/// This value can be up to 0xFFFFFF (6 hex digits).
612	///
613	/// Asserts: The token is [Kind::UnicodeRange].
614	#[inline]
615	pub const fn unicode_range_start(&self) -> u32 {
616		debug_assert!(self.kind_bits() == Kind::UnicodeRange as u8);
617		self.0 & LENGTH_MASK
618	}
619
620	/// If the [Token] is [Kind::UnicodeRange], returns the end value of the range.
621	/// This value can be up to 0xFFFFFF (6 hex digits).
622	///
623	/// Asserts: The token is [Kind::UnicodeRange].
624	#[inline]
625	pub const fn unicode_range_end(&self) -> u32 {
626		debug_assert!(self.kind_bits() == Kind::UnicodeRange as u8);
627		self.1 & LENGTH_MASK
628	}
629
630	/// Creates a new [Kind::Delim] token.
631	#[inline]
632	pub(crate) const fn new_delim(char: char) -> Self {
633		let flags: u32 = Kind::Delim as u32;
634		Self((flags << 24) & KIND_MASK | Self::delim_value_data(char), char as u32)
635	}
636
637	/// Creates a new [Kind::Delim] token with associated whitespace.
638	#[inline]
639	pub(crate) const fn new_delim_with_associated_whitespace(char: char, rules: AssociatedWhitespaceRules) -> Self {
640		let flags: u32 = Kind::Delim as u32 | ((rules.to_bits() as u32) << 5);
641		Self((flags << 24) & KIND_MASK | Self::delim_value_data(char), char as u32)
642	}
643
644	const fn delim_value_data(char: char) -> u32 {
645		let code_point = char as usize;
646		if code_point < SINGLE_CHAR_KINDS.len() {
647			(SINGLE_CHAR_KINDS[code_point] as u32) ^ (Kind::Delim as u32)
648		} else {
649			0
650		}
651	}
652
653	/// \[private\]
654	/// Creates a new Token with an interned string.
655	#[inline]
656	pub fn new_interned(kind: Kind, bits: u32, len: u32) -> Token {
657		debug_assert!(kind == KindSet::IDENT_LIKE);
658		debug_assert!(bits & LENGTH_MASK == bits);
659		debug_assert!(len > 0);
660		Self(((kind as u32) << 24) & KIND_MASK | (bits & LENGTH_MASK), len + ((kind != Kind::Ident) as u32))
661	}
662
663	/// Returns the raw bits representing the [Kind].
664	#[inline(always)]
665	pub(crate) const fn kind_bits(&self) -> u8 {
666		(self.0 >> 24 & 0b1_1111) as u8
667	}
668
669	/// Returns the [Kind].
670	#[inline]
671	pub const fn kind(&self) -> Kind {
672		let delim_value_data = if self.is_delim_like() { self.0 as u8 } else { 0 };
673		Kind::from_bits(self.kind_bits() ^ delim_value_data)
674	}
675
676	/// Check if the TF upper-most bit is set.
677	#[inline(always)]
678	const fn first_flag(&self) -> bool {
679		self.0 >> 31 == 1
680	}
681
682	/// Check if the TF second-upper-most bit is set.
683	#[inline(always)]
684	const fn second_flag(&self) -> bool {
685		self.0 >> 30 & 0b1 == 1
686	}
687
688	/// Check if the TF third-upper-most bit is set.
689	#[inline(always)]
690	const fn third_flag(&self) -> bool {
691		self.0 >> 29 & 0b1 == 1
692	}
693
694	/// Check if the [Kind] is "Ident Like", i.e. it is [Kind::Ident], [Kind::AtKeyword], [Kind::Function], [Kind::Hash].
695	#[inline(always)]
696	pub(crate) const fn is_ident_like(&self) -> bool {
697		self.kind_bits() & 0b1100 == 0b1000
698	}
699
700	/// Check if the [Kind] is "Delim Like", i.e. it is [Kind::Delim], [Kind::Colon], [Kind::Semicolon], [Kind::Comma],
701	/// [Kind::LeftSquare], [Kind::RightSquare], [Kind::LeftParen], [Kind::RightParen], [Kind::LeftCurly],
702	/// [Kind::RightCurly].
703	#[inline(always)]
704	pub(crate) const fn is_delim_like(&self) -> bool {
705		self.kind_bits() & 0b1111 == Kind::Delim as u8
706	}
707
708	/// The only token with an empty length is EOF, but this method is available for symmetry with `len()`.
709	#[inline]
710	pub const fn is_empty(&self) -> bool {
711		self.kind_bits() == Kind::Eof as u8
712	}
713
714	/// Returns the amount of characters (utf-8 code points) this Token represents in the underlying source text.
715	#[inline]
716	pub const fn len(&self) -> u32 {
717		if self.kind_bits() == Kind::Eof as u8 {
718			0
719		} else if self.is_delim_like() {
720			debug_assert!(matches!(
721				self.kind(),
722				Kind::Delim
723					| Kind::Colon | Kind::Semicolon
724					| Kind::Comma | Kind::LeftSquare
725					| Kind::RightSquare
726					| Kind::LeftParen
727					| Kind::RightParen
728					| Kind::LeftCurly
729					| Kind::RightCurly
730			));
731			self.char().unwrap().len_utf8() as u32
732		} else if self.kind_bits() & 0b1111 == Kind::Number as u8 {
733			self.numeric_len()
734		} else if self.kind_bits() & 0b1111 == Kind::Dimension as u8 {
735			if self.first_flag() {
736				self.numeric_len() + (self.0 >> 7 & 0b11111)
737			} else {
738				((self.0 & LENGTH_MASK) >> 12) + (self.0 & !HALF_LENGTH_MASK)
739			}
740		} else if self.kind_bits() & 0b1111 == Kind::Hash as u8 {
741			self.0 & LENGTH_MASK
742		} else if self.kind_bits() == Kind::UnicodeRange as u8 {
743			self.1 >> 24
744		} else {
745			self.1
746		}
747	}
748
749	/// If the [Kind] is "Delim Like" (i.e. it is [Kind::Delim], [Kind::Colon], [Kind::Semicolon], [Kind::Comma],
750	/// [Kind::LeftSquare], [Kind::RightSquare], [Kind::LeftParen], [Kind::RightParen], [Kind::LeftCurly],
751	/// [Kind::RightCurly]) then this will return a [Some] with a [char] representing the value.
752	/// For non-delim-like tokens this will return [None].
753	pub const fn char(&self) -> Option<char> {
754		if self.is_delim_like() {
755			return char::from_u32(self.1);
756		}
757		None
758	}
759
760	/// The [Token] is a [Kind::Dimension] or [Kind::Number] and is an integer - i.e. it has no `.`.
761	#[inline]
762	pub const fn is_int(&self) -> bool {
763		self.kind_bits() & 0b1110 == 0b0100 && !self.third_flag()
764	}
765
766	/// The [Token] is a [Kind::Dimension] or [Kind::Number] and is a float - i.e. it has decimal places. This will be
767	/// `true` even if the decimal places are 0. e.g. `0.0`.
768	#[inline]
769	pub const fn is_float(&self) -> bool {
770		self.kind_bits() & 0b1100 == 0b0100 && self.third_flag()
771	}
772
773	/// The [Token] is a [Kind::Dimension] or [Kind::Number] and the underlying character data included a `-` or `+`
774	/// character. Note that a positive number may not necessarily have a sign, e.g. `3` will return false, while `+3`
775	/// will return `true`.
776	#[inline]
777	pub const fn has_sign(&self) -> bool {
778		self.kind_bits() & 0b1100 == 0b0100 && self.second_flag()
779	}
780
781	/// The [Token] is a [Kind::Number] and the `+` sign is semantically required and should be preserved during
782	/// minification. This is used for numbers in `an+b` syntax (e.g., `:nth-child(+5)`) where the `+` sign
783	/// distinguishes the value from other syntactic forms.
784	///
785	/// Asserts: the `kind()` is [Kind::Number].
786	#[inline]
787	pub const fn sign_is_required(&self) -> bool {
788		debug_assert!(self.kind_bits() == Kind::Number as u8);
789		self.first_flag()
790	}
791
792	/// Returns a new [Token] with the `sign_is_required` flag set. This indicates that the `+` sign
793	/// should be preserved during minification (e.g., for `an+b` syntax).
794	///
795	/// Asserts: the `kind()` is [Kind::Number].
796	#[inline]
797	pub const fn with_sign_required(self) -> Token {
798		debug_assert!(self.kind_bits() == Kind::Number as u8);
799		Token(self.0 | (1 << 31), self.1)
800	}
801
802	/// If the [Token] is a [Kind::Dimension] or [Kind::Number] then this returns the amount of characters used to
803	/// represent this number in the underlying source text. Numbers may be inefficiently encoded in the source text,
804	/// e.g. `0.0000`.
805	///
806	/// Asserts: the `kind()` is [Kind::Dimension] or [Kind::Number].
807	#[inline]
808	pub const fn numeric_len(&self) -> u32 {
809		debug_assert!(matches!(self.kind(), Kind::Number | Kind::Dimension | Kind::BadNumber | Kind::BadDimension));
810		if self.kind_bits() & 0b1111 == Kind::Dimension as u8 {
811			(self.0 & LENGTH_MASK) >> 12
812		} else {
813			self.0 & LENGTH_MASK
814		}
815	}
816
817	/// If the [Token] is a [Kind::Dimension] or [Kind::Number] then this returns the [f32] representation of the number's
818	/// value.
819	///
820	/// Asserts: the `kind()` is [Kind::Dimension] or [Kind::Number].
821	#[inline]
822	pub fn value(&self) -> f32 {
823		debug_assert!(matches!(self.kind(), Kind::Number | Kind::Dimension));
824		f32::from_bits(self.1)
825	}
826
827	/// Returns the [Whitespace].
828	///
829	/// If the [Token] is not a [Kind::Whitespace] this will return [Whitespace::none()].
830	#[inline]
831	pub fn whitespace_style(&self) -> Whitespace {
832		if self.kind_bits() == Kind::Whitespace as u8 {
833			Whitespace::from_bits((self.0 >> 29) as u8)
834		} else {
835			Whitespace::none()
836		}
837	}
838
839	/// If the [Token] is a [Kind::Whitespace] then this returns true if that whitespace is significant; i.e. it must be
840	/// preserved during minification. Descendant combinators (`a b`) and the space in `@charset "utf-8";` are examples
841	/// of this.
842	///
843	/// If the [Token] is not a [Kind::Whitespace] this will return `false`.
844	#[inline]
845	pub const fn whitespace_is_significant(&self) -> bool {
846		self.kind_bits() == Kind::Whitespace as u8 && self.0 & 1 == 1
847	}
848
849	/// Returns a new [Token] with the `whitespace_is_significant` flag set.
850	///
851	/// If the [Token] is not a [Kind::Whitespace] this will return the same [Token].
852	#[inline]
853	pub const fn with_significant_whitespace(&self, significant: bool) -> Token {
854		if self.kind_bits() != Kind::Whitespace as u8 {
855			return *self;
856		}
857		if significant { Token(self.0 | 1, self.1) } else { Token(self.0 & !1, self.1) }
858	}
859
860	/// Returns the [AssociatedWhitespaceRules].
861	///
862	/// If the [Kind] is not "Delim Like" (i.e. it is not [Kind::Delim], [Kind::Colon], [Kind::Semicolon], [Kind::Comma],
863	/// [Kind::LeftSquare], [Kind::RightSquare], [Kind::LeftParen], [Kind::RightParen], [Kind::LeftCurly],
864	/// [Kind::RightCurly]) then this will always return `AssociatedWhitespaceRules::none()`.
865	#[inline]
866	pub fn associated_whitespace(&self) -> AssociatedWhitespaceRules {
867		if self.is_delim_like() {
868			AssociatedWhitespaceRules::from_bits((self.0 >> 29) as u8)
869		} else {
870			AssociatedWhitespaceRules::none()
871		}
872	}
873
874	/// Returns a new [Token] with the [AssociatedWhitespaceRules] set to the given [AssociatedWhitespaceRules],
875	/// if possible.
876	///
877	/// If the [Kind] is not "Delim Like" (i.e. it is not [Kind::Delim], [Kind::Colon], [Kind::Semicolon], [Kind::Comma],
878	/// [Kind::LeftSquare], [Kind::RightSquare], [Kind::LeftParen], [Kind::RightParen], [Kind::LeftCurly],
879	/// [Kind::RightCurly]) then this will return the same [Token].
880	/// If the [AssociatedWhitespaceRules] is different it will return a new [Token].
881	#[inline]
882	pub fn with_associated_whitespace(&self, rules: AssociatedWhitespaceRules) -> Token {
883		if !self.is_delim_like() {
884			return *self;
885		}
886		Token::new_delim_with_associated_whitespace(self.char().unwrap(), rules)
887	}
888
889	/// Returns the [CommentStyle].
890	///
891	/// If the [Token] is not a [Kind::Comment] this will return [None].
892	#[inline]
893	pub fn comment_style(&self) -> Option<CommentStyle> {
894		if self.kind_bits() == Kind::Comment as u8 { CommentStyle::from_bits((self.0 >> 29) as u8) } else { None }
895	}
896
897	/// Returns the [QuoteStyle].
898	///
899	/// If the [Token] is not a [Kind::String] this will return [QuoteStyle::None].
900	#[inline]
901	pub fn quote_style(&self) -> QuoteStyle {
902		if self.kind_bits() == Kind::String as u8 {
903			if self.third_flag() {
904				return QuoteStyle::Double;
905			} else {
906				return QuoteStyle::Single;
907			}
908		}
909		QuoteStyle::None
910	}
911
912	/// Returns a new [Token] with the [QuoteStyle] set to the given [QuoteStyle], if possible.
913	///
914	/// If the [Token] is not a [Kind::String], or the [QuoteStyle] is already the given [QuoteStyle] this will return the same [Token].
915	/// If the [QuoteStyle] is different it will return a new [Token].
916	/// [QuoteStyle] must not be [QuoteStyle::None]
917	#[inline]
918	pub fn with_quotes(&self, quote_style: QuoteStyle) -> Token {
919		debug_assert!(quote_style != QuoteStyle::None);
920		if self.kind_bits() != Kind::String as u8 || quote_style == self.quote_style() {
921			return *self;
922		}
923		Token::new_string(quote_style, self.has_close_quote(), self.contains_escape_chars(), self.len())
924	}
925
926	/// If the [Token] is a [Kind::String] this checks if the string ended in a close quote.
927	/// It is possible to have a valid String token that does not end in a close quote, by eliding the quote at the end of
928	/// a file.
929	///
930	/// Asserts: The [Kind] is [Kind::String].
931	#[inline]
932	pub const fn has_close_quote(&self) -> bool {
933		debug_assert!(self.kind_bits() == Kind::String as u8);
934		self.second_flag()
935	}
936
937	/// Checks if it is possible for the [Token] to contain escape characters. Numbers, for example, cannot. Idents can.
938	#[inline]
939	pub const fn can_escape(&self) -> bool {
940		self.kind_bits() == Kind::String as u8
941			|| self.kind_bits() == Kind::Url as u8
942			|| self.kind_bits() == Kind::Dimension as u8
943			|| self.is_ident_like()
944	}
945
946	/// If the [Token] can escape, checks if the underlying source text contained escape characters.
947	///
948	/// Asserts: The token can escape ([Token::can_escape()]).
949	#[inline]
950	pub const fn contains_escape_chars(&self) -> bool {
951		if self.kind_bits() == Kind::Dimension as u8 {
952			// Always assume Dimension contains escape because we have other fast paths to handle dimension units
953			return true;
954		}
955		self.can_escape() && self.first_flag()
956	}
957
958	/// If the [Token] is Ident like, checks if the first two code points are HYPHEN-MINUS (`-`).
959	///
960	/// Asserts: The token is "ident like", i.e. it is [Kind::Ident], [Kind::AtKeyword], [Kind::Function], [Kind::Hash].
961	#[inline]
962	pub const fn is_dashed_ident(&self) -> bool {
963		debug_assert!(self.is_ident_like());
964		self.second_flag()
965	}
966
967	/// Checks if the [Token] is Ident like and none of the characters are ASCII upper-case.
968	#[inline]
969	pub const fn is_lower_case(&self) -> bool {
970		self.is_ident_like() && !self.third_flag()
971	}
972
973	#[inline]
974	pub fn atom_bits(&self) -> u32 {
975		if self.kind_bits() & 0b1111 == Kind::Dimension as u8 && self.first_flag() {
976			self.0 & 0b111_1111
977		} else if self.is_ident_like() && self.kind_bits() & 0b1111 != Kind::Hash as u8 {
978			self.0 & LENGTH_MASK
979		} else {
980			0
981		}
982	}
983
984	/// Checks if the [Token] is Trivia-like, that is [Kind::Comment], [Kind::Whitespace], [Kind::Eof]
985	#[inline]
986	pub const fn is_trivia(&self) -> bool {
987		self.kind_bits() & 0b000011 == self.kind_bits()
988	}
989
990	/// If the [Token] is [Kind::Url], checks if there are leading Whitespace characters before the inner value.
991	///
992	/// Asserts: The token is [Kind::Url].
993	#[inline]
994	pub const fn url_has_leading_space(&self) -> bool {
995		debug_assert!(self.kind_bits() == Kind::Url as u8);
996		self.second_flag()
997	}
998
999	/// If the [Token] is [Kind::Url], checks if the closing parenthesis is present.
1000	///
1001	/// Asserts: The token is [Kind::Url].
1002	#[inline]
1003	pub const fn url_has_closing_paren(&self) -> bool {
1004		debug_assert!(self.kind_bits() == Kind::Url as u8);
1005		self.third_flag()
1006	}
1007
1008	/// If the [Token] is [Kind::Hash], checks if the Hash is "ID-like" (i.e its first character is ASCII).
1009	///
1010	/// Asserts: The token is [Kind::Hash].
1011	#[inline]
1012	pub const fn hash_is_id_like(&self) -> bool {
1013		debug_assert!(self.kind_bits() == Kind::Hash as u8);
1014		self.second_flag()
1015	}
1016
1017	/// Checks if the [Token] is [Kind::BadString] or [Kind::BadUrl], or the "bad flag" has been set.
1018	#[inline]
1019	pub const fn is_bad(&self) -> bool {
1020		self.kind_bits() & 0b1_0000 == 0b1_0000
1021	}
1022
1023	/// Returns a new token with the bad/recovery flag set.
1024	/// This is used by the parser to mark tokens as problematic during error recovery.
1025	#[inline]
1026	pub const fn with_bad_flag(&self) -> Self {
1027		Self(self.0 | 1 << 28, self.1)
1028	}
1029
1030	/// Checks if the [Token] is [Kind::CdcOrCdo] and is the CDC variant of that token.
1031	#[inline]
1032	pub const fn is_cdc(&self) -> bool {
1033		self.kind_bits() == (Kind::CdcOrCdo as u8) && self.third_flag()
1034	}
1035
1036	/// Some tokens may have a "leading" part:
1037	///  - [Kind::AtKeyword] always starts with a `@`,
1038	///  - [Kind::Hash] with a `#`.
1039	///  - [Kind::String] with a `"` or `'`.
1040	///  - [Kind::Comment] with a leading `/*` (or `//`).
1041	///  - [Kind::Dimension] has a leading numeric portion.
1042	///  - [Kind::Url] has the leading `url(` ident (which may vary in exact representation).
1043	///
1044	/// This function returns the length of that, irrespective of the [Kind]. For other kinds not listed, this will return
1045	/// `0`, but for the above kinds it will calculate the leading length. This is useful for parsing out the underlying
1046	/// data which is likely to be of greater use.
1047	pub fn leading_len(&self) -> u32 {
1048		match self.kind() {
1049			Kind::AtKeyword | Kind::Hash | Kind::String | Kind::BadAtKeyword | Kind::BadHash | Kind::BadString => 1,
1050			Kind::Dimension | Kind::BadDimension => self.numeric_len(),
1051			Kind::Comment | Kind::BadComment => 2,
1052			Kind::Url | Kind::BadUrl => (self.0 & LENGTH_MASK) >> 12,
1053			_ => 0,
1054		}
1055	}
1056
1057	/// Some tokens may have a "trailing" part:
1058	///  - [Kind::Function] will always have an opening `(`.
1059	///  - [Kind::String] may have a closing `"` or `'`.
1060	///  - [Kind::Comment] may have a closing `*/`
1061	///  - [Kind::Url] may have a clsoing `)`.
1062	///
1063	/// This function returns the length of that, irrespective of the [Kind]. For other kinds not listed, this will return
1064	/// `0`, but for the above kinds it will calculate the leading length. This is useful for parsing out the underlying
1065	/// data which is likely to be of greater use.
1066	pub fn trailing_len(&self) -> u32 {
1067		match self.kind() {
1068			Kind::Function | Kind::BadFunction => 1,
1069			Kind::String | Kind::BadString => self.has_close_quote() as u32,
1070			Kind::Comment | Kind::BadComment if self.comment_style().unwrap().is_block() => 2,
1071			Kind::Url | Kind::BadUrl => self.0 & !HALF_LENGTH_MASK,
1072			_ => 0,
1073		}
1074	}
1075
1076	/// Certain kinds have a [PairWise] equivalent:
1077	///  - [Kind::LeftParen] has [Kind::RightParen]
1078	///  - [Kind::LeftCurly] has [Kind::RightCurly]
1079	///  - [Kind::LeftSquare] has [Kind::RightSquare]
1080	///
1081	/// This function returns the [PairWise] enum, if the [Token] is one of the above listed [Kinds][Kind]. For any other
1082	/// [Kind] this returns [None].
1083	#[inline]
1084	pub fn to_pairwise(&self) -> Option<PairWise> {
1085		PairWise::from_token(self)
1086	}
1087
1088	/// A convenience function for `Cursor::new(offset, token)`.
1089	#[inline(always)]
1090	pub fn with_cursor(self, offset: SourceOffset) -> Cursor {
1091		Cursor::new(offset, self)
1092	}
1093
1094	/// If the [Kind] is [Kind::Hash] then this token may have had the opportunity to be parsed as a `<hex-value>` (e.g.
1095	/// `#fff`). When this happens the character data is parsed during tokenization into a u32 which stores the
1096	/// RR,GG,BB,AA values.
1097	#[inline(always)]
1098	pub fn hex_value(self) -> u32 {
1099		if self == Kind::Hash { self.1 } else { 0 }
1100	}
1101
1102	/// If this [Token] is preceded by the [Token] `other` then a separating token (e.g. a comment) will need to be
1103	/// inserted between these the two tokens during serialization, in order for them to be able to be re-tokenized as
1104	/// the same tokens. For example an Ident ("a") adjacent to an Ident ("b"), if serialized without whitespace, would
1105	/// create a single Ident ("ab"). The rules for estbalishing whether or not these tokens needs whitespace are quite
1106	/// simple and are effectively [defined in the serialization section of the spec][1]. To reproduce the table:
1107	///
1108	/// [1]: https://drafts.csswg.org/css-syntax/#serialization
1109	///
1110	/// |            | ident | function | url | bad url | - | number | percentage | dimension | CDC | ( | * | % |
1111	/// |:-----------|:-----:|:--------:|:---:|:-------:|:-:|:------:|:----------:|:---------:|:---:|:-:|:-:|:-:|
1112	/// | ident      |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |    ✗   |      ✗     |     ✗     |  ✗  | ✗ |   |   |
1113	/// | at-keyword |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |    ✗   |      ✗     |     ✗     |  ✗  |   |   |   |
1114	/// | hash       |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |    ✗   |      ✗     |     ✗     |  ✗  |   |   |   |
1115	/// | dimension  |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |    ✗   |      ✗     |     ✗     |  ✗  |   |   |   |
1116	/// | #          |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |    ✗   |      ✗     |     ✗     |  ✗  |   |   |   |
1117	/// | \-         |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |    ✗   |      ✗     |     ✗     |  ✗  |   |   |   |
1118	/// | number     |   ✗   |    ✗     |  ✗  |    ✗    |   |    ✗   |      ✗     |     ✗     |  ✗  |   |   | ✗ |
1119	/// | @          |   ✗   |    ✗     |  ✗  |    ✗    | ✗ |        |            |           |  ✗  |   |   |   |
1120	/// | .          |       |          |     |         |   |    ✗   |      ✗     |     ✗     |     |   |   |   |
1121	/// | +          |       |          |     |         |   |    ✗   |      ✗     |     ✗     |     |   |   |   |
1122	/// | /          |       |          |     |         |   |        |            |           |     |   | ✗ |   |
1123	///
1124	/// The one exception not in this table is that two consecutive `/` characters should also be separated by spaces in
1125	/// order to avoid abmiguities with CSS-alike languages that treat two consecutive `/` characters as a single line
1126	/// comment.
1127	///
1128	/// # Example
1129	///
1130	/// ```
1131	/// use css_lexer::*;
1132	/// let mut lexer = Lexer::new(&EmptyAtomSet::ATOMS, "10 %");
1133	/// let first = lexer.advance();
1134	/// let _ = lexer.advance(); // Whitespace
1135	/// let second = lexer.advance();
1136	/// assert!(first.needs_separator_for(second));
1137	/// ```
1138	pub fn needs_separator_for(&self, second: Token) -> bool {
1139		if second == AssociatedWhitespaceRules::EnforceBefore && *self != Kind::Whitespace
1140			|| *self == AssociatedWhitespaceRules::EnforceAfter && second != Kind::Whitespace
1141		{
1142			// We need whitespace after, unless the next token is actually whitespace.
1143			return true;
1144		}
1145		if *self == AssociatedWhitespaceRules::BanAfter {
1146			return false;
1147		}
1148		match self.kind() {
1149			Kind::Ident => {
1150				(matches!(second.kind(), Kind::Number | Kind::Dimension) &&
1151					// numbers with a `-` need separating, but with `+` they do not.
1152					(!second.has_sign() || second.value() < 0.0))
1153					|| matches!(second.kind(), Kind::Ident | Kind::Function | Kind::Url | Kind::BadUrl)
1154					|| matches!(second.char(), Some('(' | '-'))
1155					|| second.is_cdc()
1156			}
1157			Kind::AtKeyword | Kind::Hash | Kind::Dimension => {
1158				(matches!(second.kind(), Kind::Number | Kind::Dimension) &&
1159					// numbers with a `-` need separating, but with `+` they do not.
1160					(!second.has_sign() || second.value() < 0.0))
1161					|| matches!(second.kind(), Kind::Ident | Kind::Function | Kind::Url | Kind::BadUrl)
1162					|| matches!(second.char(), Some('-'))
1163					|| second.is_cdc()
1164			}
1165			Kind::Number => {
1166				matches!(
1167					second.kind(),
1168					Kind::Ident | Kind::Function | Kind::Url | Kind::BadUrl | Kind::Number | Kind::Dimension
1169				) || matches!(second.char(), Some('%'))
1170					|| second.is_cdc()
1171			}
1172			_ => match self.char() {
1173				Some('#') => {
1174					matches!(
1175						second.kind(),
1176						Kind::Ident | Kind::Function | Kind::Url | Kind::BadUrl | Kind::Number | Kind::Dimension
1177					) || matches!(second.char(), Some('-'))
1178						|| second.is_cdc()
1179				}
1180				Some('-') => {
1181					matches!(
1182						second.kind(),
1183						Kind::Ident | Kind::Function | Kind::Url | Kind::BadUrl | Kind::Number | Kind::Dimension
1184					) || matches!(second.char(), Some('-'))
1185						|| second.is_cdc()
1186				}
1187				Some('@') => {
1188					matches!(second.kind(), Kind::Ident | Kind::Function | Kind::Url | Kind::BadUrl)
1189						|| matches!(second.char(), Some('-'))
1190						|| second.is_cdc()
1191				}
1192				Some('.') => matches!(second.kind(), Kind::Number | Kind::Dimension),
1193				Some('+') => matches!(second.kind(), Kind::Number | Kind::Dimension),
1194				Some('/') => matches!(second.char(), Some('*' | '/')),
1195				_ => false,
1196			},
1197		}
1198	}
1199
1200	pub fn to_bits(&self) -> u64 {
1201		(self.0 as u64) << 32 | self.1 as u64
1202	}
1203}
1204
1205impl core::fmt::Debug for Token {
1206	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1207		let mut d = f.debug_struct(format!("Token::{}", self.kind().as_str()).as_str());
1208		match self.kind() {
1209			Kind::Eof => &mut d,
1210			Kind::Number => d.field("value", &self.value()).field("len", &self.numeric_len()),
1211			Kind::Dimension => {
1212				d.field("value", &self.value()).field("len", &self.numeric_len()).field("dimension_len", &self.len())
1213			}
1214			_ if self.is_delim_like() => {
1215				d.field("char", &self.char().unwrap()).field("len", &self.len());
1216				if !self.associated_whitespace().is_none() {
1217					d.field("associated_whitespace", &self.associated_whitespace());
1218				}
1219				&mut d
1220			}
1221			Kind::String => d
1222				.field("quote_style", &if self.first_flag() { "Double" } else { "Single" })
1223				.field("has_close_quote", &self.second_flag())
1224				.field("contains_escape_chars", &self.third_flag())
1225				.field("len", &self.len()),
1226			Kind::Ident | Kind::Function | Kind::AtKeyword => d
1227				.field("is_lower_case", &self.first_flag())
1228				.field("is_dashed_ident", &self.second_flag())
1229				.field("contains_escape_chars", &self.third_flag())
1230				.field("len", &self.len()),
1231			Kind::Hash => d
1232				.field("is_lower_case", &self.first_flag())
1233				.field("hash_is_id_like", &self.second_flag())
1234				.field("contains_escape_chars", &self.third_flag())
1235				.field("len", &self.len()),
1236			Kind::Url => d
1237				.field("url_has_closing_paren", &self.first_flag())
1238				.field("url_has_leading_space", &self.second_flag())
1239				.field("contains_escape_chars", &self.third_flag())
1240				.field("len", &self.len()),
1241			Kind::UnicodeRange => d
1242				.field("start", &format_args!("U+{:X}", self.unicode_range_start()))
1243				.field("end", &format_args!("U+{:X}", self.unicode_range_end()))
1244				.field("len", &self.len()),
1245			Kind::CdcOrCdo => d.field("is_cdc", &self.first_flag()).field("len", &self.len()),
1246			Kind::Whitespace => {
1247				d.field("contains", &self.whitespace_style());
1248				if self.whitespace_is_significant() {
1249					d.field("significant", &true);
1250				}
1251				d.field("len", &self.len())
1252			}
1253			_ => d
1254				.field("flag_0", &self.first_flag())
1255				.field("flag_1", &self.second_flag())
1256				.field("flag_2", &self.third_flag())
1257				.field("len", &self.len()),
1258		}
1259		.finish()
1260	}
1261}
1262
1263impl std::fmt::Display for Token {
1264	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1265		match self.kind() {
1266			Kind::Delim => write!(f, "Delim({})", self.char().unwrap()),
1267			k => write!(f, "{}", k.as_str()),
1268		}
1269	}
1270}
1271
1272#[cfg(feature = "serde")]
1273impl serde::ser::Serialize for Token {
1274	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1275	where
1276		S: serde::ser::Serializer,
1277	{
1278		use serde::ser::SerializeStruct;
1279		if *self == Self::EMPTY {
1280			return serializer.serialize_none();
1281		}
1282		let mut state = serializer.serialize_struct("Token", 3)?;
1283		state.serialize_field("kind", self.kind().as_str())?;
1284		state.serialize_field("len", &self.len())?;
1285		state.end()
1286	}
1287}
1288
1289impl From<Token> for Kind {
1290	fn from(token: Token) -> Self {
1291		token.kind()
1292	}
1293}
1294
1295impl PartialEq<Kind> for Token {
1296	fn eq(&self, other: &Kind) -> bool {
1297		self.kind() == *other
1298	}
1299}
1300
1301impl From<Token> for KindSet {
1302	fn from(token: Token) -> Self {
1303		KindSet::new(&[token.kind()])
1304	}
1305}
1306
1307impl PartialEq<KindSet> for Token {
1308	fn eq(&self, other: &KindSet) -> bool {
1309		other.contains(self.kind())
1310	}
1311}
1312
1313impl From<Token> for QuoteStyle {
1314	fn from(token: Token) -> Self {
1315		token.quote_style()
1316	}
1317}
1318
1319impl PartialEq<QuoteStyle> for Token {
1320	fn eq(&self, other: &QuoteStyle) -> bool {
1321		&self.quote_style() == other
1322	}
1323}
1324
1325impl From<Token> for Whitespace {
1326	fn from(token: Token) -> Self {
1327		token.whitespace_style()
1328	}
1329}
1330
1331impl PartialEq<Whitespace> for Token {
1332	fn eq(&self, other: &Whitespace) -> bool {
1333		self.whitespace_style().intersects(*other)
1334	}
1335}
1336
1337impl PartialEq<AssociatedWhitespaceRules> for Token {
1338	fn eq(&self, other: &AssociatedWhitespaceRules) -> bool {
1339		self.associated_whitespace().intersects(*other)
1340	}
1341}
1342
1343impl PartialEq<CommentStyle> for Token {
1344	fn eq(&self, other: &CommentStyle) -> bool {
1345		self.comment_style().map(|style| &style == other).unwrap_or(false)
1346	}
1347}
1348
1349impl PartialEq<PairWise> for Token {
1350	fn eq(&self, other: &PairWise) -> bool {
1351		self.to_pairwise().map(|style| &style == other).unwrap_or(false)
1352	}
1353}
1354
1355impl PartialEq<char> for Token {
1356	fn eq(&self, other: &char) -> bool {
1357		self.char().map(|char| char == *other).unwrap_or(false)
1358	}
1359}
1360
1361#[test]
1362fn size_test() {
1363	assert_eq!(::std::mem::size_of::<Token>(), 8);
1364}
1365
1366#[test]
1367fn test_new_whitespace() {
1368	assert_eq!(Token::SPACE, Kind::Whitespace);
1369	assert_eq!(Token::SPACE, Whitespace::Space);
1370	assert_eq!(Token::TAB, Kind::Whitespace);
1371	assert_eq!(Token::TAB, Whitespace::Tab);
1372	assert_eq!(Token::NEWLINE, Kind::Whitespace);
1373	assert_eq!(Token::NEWLINE, Whitespace::Newline);
1374	assert_eq!(Token::new_whitespace(Whitespace::Space, 4), Kind::Whitespace);
1375	assert_eq!(Token::new_whitespace(Whitespace::Space | Whitespace::Newline, 4), Whitespace::Space);
1376	assert_eq!(Token::new_whitespace(Whitespace::Space, 4).len(), 4);
1377	assert_eq!(Token::new_whitespace(Whitespace::Tab | Whitespace::Space, 4), Whitespace::Tab);
1378	assert_eq!(Token::new_whitespace(Whitespace::Newline, 4), Whitespace::Newline);
1379	assert_eq!(Token::new_whitespace(Whitespace::Newline, 4).len(), 4);
1380}
1381
1382#[test]
1383fn test_new_comment() {
1384	assert_eq!(Token::new_comment(CommentStyle::Block, 4), Kind::Comment);
1385	assert_eq!(Token::new_comment(CommentStyle::Block, 4), CommentStyle::Block);
1386	assert_eq!(Token::new_comment(CommentStyle::Single, 4), CommentStyle::Single);
1387}
1388
1389#[test]
1390fn test_new_number() {
1391	assert_eq!(Token::new_number(false, false, 3, 4.2), Kind::Number);
1392	assert_eq!(Token::new_number(false, false, 3, 4.2).value(), 4.2);
1393	assert_eq!(Token::new_number(false, false, 3, 4.2).len(), 3);
1394	assert_eq!(Token::new_number(false, true, 9, 4.2), Kind::Number);
1395	assert_eq!(Token::new_number(false, true, 9, 4.2).value(), 4.2);
1396	assert_eq!(Token::new_number(false, true, 9, 4.2).len(), 9);
1397	assert!(!Token::new_number(false, false, 3, 4.2).has_sign());
1398	assert!(Token::new_number(false, true, 3, 4.2).has_sign());
1399	assert!(!Token::new_number(false, true, 3, 4.0).is_float());
1400	assert!(Token::new_number(true, false, 3, 4.2).is_float());
1401}
1402
1403#[test]
1404fn test_new_string() {
1405	assert_eq!(Token::new_string(QuoteStyle::Single, false, false, 4), Kind::String);
1406	assert_eq!(Token::new_string(QuoteStyle::Single, false, false, 4), QuoteStyle::Single);
1407	assert!(!Token::new_string(QuoteStyle::Single, false, false, 4).has_close_quote());
1408	assert!(!Token::new_string(QuoteStyle::Single, false, false, 4).contains_escape_chars());
1409	assert_eq!(Token::new_string(QuoteStyle::Single, false, false, 4).len(), 4);
1410	assert_eq!(Token::new_string(QuoteStyle::Double, false, false, 4), Kind::String);
1411	assert_eq!(Token::new_string(QuoteStyle::Double, false, false, 4), QuoteStyle::Double);
1412	assert!(Token::new_string(QuoteStyle::Double, true, false, 4).has_close_quote());
1413	assert!(!Token::new_string(QuoteStyle::Double, true, false, 4).contains_escape_chars());
1414	assert_eq!(Token::new_string(QuoteStyle::Double, true, false, 5).len(), 5);
1415	assert!(Token::new_string(QuoteStyle::Double, true, true, 4).contains_escape_chars());
1416	assert!(Token::new_string(QuoteStyle::Double, false, true, 4).contains_escape_chars());
1417}
1418
1419#[test]
1420fn test_new_hash() {
1421	assert_eq!(Token::new_hash(false, false, false, 4, 0), Kind::Hash);
1422	assert!(!Token::new_hash(false, false, false, 4, 0).contains_escape_chars());
1423	assert!(Token::new_hash(false, false, true, 4, 0).contains_escape_chars());
1424	assert!(Token::new_hash(false, false, true, 4, 0).is_lower_case());
1425	assert!(!Token::new_hash(true, false, false, 4, 0).is_lower_case());
1426	assert_eq!(Token::new_hash(true, false, false, 4, 0).len(), 4);
1427	assert_eq!(Token::new_hash(true, false, false, 4, 0).hex_value(), 0);
1428	assert_eq!(Token::new_hash(true, false, false, 4, 18).hex_value(), 18);
1429}
1430
1431#[test]
1432#[should_panic]
1433fn test_new_string_with_quotes_none() {
1434	Token::new_string(QuoteStyle::None, false, true, 4);
1435}
1436
1437#[test]
1438fn test_new_delim() {
1439	assert_eq!(Token::new_delim('>'), Kind::Delim);
1440	assert_eq!(Token::new_delim('>'), '>');
1441	assert_eq!(Token::new_delim('>').len(), 1);
1442	assert_eq!(Token::new_delim('.'), Kind::Delim);
1443	assert_eq!(Token::new_delim('.'), '.');
1444	assert_eq!(Token::new_delim('.').len(), 1);
1445	assert_eq!(Token::new_delim('ℝ'), Kind::Delim);
1446	assert_eq!(Token::new_delim('ℝ'), 'ℝ');
1447	assert_eq!(Token::new_delim('ℝ').len(), 3);
1448	assert_eq!(Token::new_delim('💣'), Kind::Delim);
1449	assert_eq!(Token::new_delim('💣'), '💣');
1450	assert_eq!(Token::new_delim('💣').len(), 4);
1451	assert_eq!(Token::new_delim('💣').len(), 4);
1452	assert_eq!(Token::new_delim('💣').len(), 4);
1453}
1454
1455#[test]
1456fn test_delim_like_kinds() {
1457	for (char, kind, bad_kind) in [
1458		(':', Kind::Colon, Kind::BadColon),
1459		(';', Kind::Semicolon, Kind::BadSemicolon),
1460		(',', Kind::Comma, Kind::BadComma),
1461		('[', Kind::LeftSquare, Kind::BadLeftSquare),
1462		(']', Kind::RightSquare, Kind::BadRightSquare),
1463		('(', Kind::LeftParen, Kind::BadLeftParen),
1464		(')', Kind::RightParen, Kind::BadRightParen),
1465		('{', Kind::LeftCurly, Kind::BadLeftCurly),
1466		('}', Kind::RightCurly, Kind::BadRightCurly),
1467		('\0', Kind::Delim, Kind::BadDelim),
1468		('>', Kind::Delim, Kind::BadDelim),
1469		('\x7f', Kind::Delim, Kind::BadDelim),
1470		('💣', Kind::Delim, Kind::BadDelim),
1471	] {
1472		let token = Token::new_delim(char);
1473		assert_eq!(token.kind(), kind);
1474		assert_eq!(token.with_bad_flag().kind(), bad_kind);
1475		assert_eq!(Token::dummy(kind).kind(), bad_kind);
1476		let spaced = token.with_associated_whitespace(AssociatedWhitespaceRules::EnforceBefore);
1477		assert_eq!(spaced.kind(), kind);
1478		assert_eq!(spaced, char);
1479	}
1480}
1481
1482#[test]
1483fn with_associated_whitespace() {
1484	assert_eq!(
1485		Token::new_delim('>').with_associated_whitespace(
1486			AssociatedWhitespaceRules::EnforceBefore | AssociatedWhitespaceRules::EnforceAfter
1487		),
1488		AssociatedWhitespaceRules::EnforceBefore | AssociatedWhitespaceRules::EnforceBefore
1489	);
1490	assert_eq!(
1491		Token::new_delim('>').with_associated_whitespace(AssociatedWhitespaceRules::BanAfter),
1492		AssociatedWhitespaceRules::BanAfter
1493	);
1494}
1495
1496#[test]
1497fn test_with_quotes() {
1498	assert_eq!(
1499		Token::new_string(QuoteStyle::Single, false, false, 4).with_quotes(QuoteStyle::Double),
1500		Token::new_string(QuoteStyle::Double, false, false, 4)
1501	);
1502	assert_eq!(
1503		Token::new_string(QuoteStyle::Double, true, true, 8).with_quotes(QuoteStyle::Single),
1504		Token::new_string(QuoteStyle::Single, true, true, 8),
1505	);
1506}
1507
1508#[test]
1509fn test_with_significant_whitespace() {
1510	for len in [1, 3, 4, 255, 256, 0xFF_FFFF, u32::MAX] {
1511		for style in [Whitespace::Space, Whitespace::Tab, Whitespace::Space | Whitespace::Newline] {
1512			let token = Token::new_whitespace(style, len);
1513			let significant = token.with_significant_whitespace(true);
1514			assert!(!token.whitespace_is_significant());
1515			assert!(significant.whitespace_is_significant());
1516			assert_eq!(token.len(), len);
1517			assert_eq!(significant.len(), len);
1518			assert_eq!(significant.whitespace_style(), style);
1519			assert_eq!(significant.kind(), Kind::Whitespace);
1520			assert_eq!(significant.with_significant_whitespace(false), token);
1521		}
1522	}
1523	let ident = Token::new_interned(Kind::Ident, 1, 3);
1524	assert_eq!(ident.with_significant_whitespace(true), ident);
1525	assert!(!ident.whitespace_is_significant());
1526}
1527
1528#[test]
1529#[should_panic]
1530fn test_with_quotes_none() {
1531	Token::new_string(QuoteStyle::Single, false, true, 4).with_quotes(QuoteStyle::None);
1532	Token::new_string(QuoteStyle::Double, false, true, 4).with_quotes(QuoteStyle::None);
1533}
1534
1535#[test]
1536fn test_new_dimension() {
1537	{
1538		let token = Token::new_dimension(false, false, 3, 3, 999.0, 0);
1539		assert_eq!(token, Kind::Dimension);
1540		assert_eq!(token.value(), 999.0);
1541		assert_eq!(token.numeric_len(), 3);
1542		assert_eq!(token.len(), 6);
1543		assert!(!token.is_float());
1544		assert!(!token.has_sign());
1545	}
1546	{
1547		let token = Token::new_dimension(false, false, 5, 2, 8191.0, 0);
1548		assert_eq!(token, Kind::Dimension);
1549		assert_eq!(token.value(), 8191.0);
1550		assert_eq!(token.numeric_len(), 5);
1551		assert_eq!(token.len(), 7);
1552		assert!(!token.is_float());
1553		assert!(!token.has_sign());
1554	}
1555	for i in -8191..8191 {
1556		let token = Token::new_dimension(false, false, 9, 3, i as f32, 0);
1557		assert_eq!(token.value(), i as f32);
1558	}
1559}
1560
1561#[test]
1562fn test_bad_bits() {
1563	let token = Token::new_dimension(false, false, 5, 2, 8191.0, 42);
1564	assert!(!token.is_bad());
1565	assert_eq!(token.kind(), Kind::Dimension);
1566	assert_eq!(Kind::from_bits(token.kind_bits()), Kind::Dimension);
1567	assert!(!token.is_bad());
1568	assert_eq!(token.len(), 7);
1569	assert_eq!(token.numeric_len(), 5);
1570	let bad_token = token.with_bad_flag();
1571	assert!(bad_token.is_bad());
1572	assert_eq!(bad_token.kind(), Kind::BadDimension);
1573	assert_eq!(Kind::from_bits(bad_token.kind_bits()), Kind::BadDimension);
1574	assert_eq!(bad_token.len(), 7);
1575	assert_eq!(bad_token.numeric_len(), 5);
1576	assert_eq!(bad_token.atom_bits(), 42);
1577
1578	let token = Token::new_delim('(');
1579	assert!(!token.is_bad());
1580	let bad_token = token.with_bad_flag();
1581	assert!(!token.is_bad());
1582	assert!(bad_token.is_bad());
1583	assert_eq!(bad_token.kind(), Kind::BadLeftParen);
1584
1585	let token = Token::new_delim('[');
1586	assert_eq!(token, Kind::LeftSquare);
1587	assert_eq!(token.with_bad_flag().kind(), Kind::BadLeftSquare);
1588}
1589
1590impl SourceTokenTrait for Token {
1591	type Kind = crate::Kind;
1592	const EMPTY: Self = Token::EMPTY;
1593
1594	fn kind(self) -> Self::Kind {
1595		Token::kind(&self)
1596	}
1597
1598	fn len(self) -> u32 {
1599		Token::len(&self)
1600	}
1601
1602	fn kind_name(self) -> &'static str {
1603		Token::kind(&self).as_str()
1604	}
1605
1606	fn leading_len(self) -> u32 {
1607		Token::leading_len(&self)
1608	}
1609
1610	fn trailing_len(self) -> u32 {
1611		Token::trailing_len(&self)
1612	}
1613}