Skip to main content

css_parse/
token_macros.rs

1use crate::{
2	Cursor, CursorSink, Kind, KindSet, Parse, Parser, Peek, Result, Span, ToNormalisedValue, ToNumberValue, Token,
3};
4
5macro_rules! cursor_wrapped {
6	($ident:ident) => {
7		impl $crate::ToCursors for $ident {
8			fn to_cursors(&self, s: &mut impl CursorSink) {
9				s.append((*self).into());
10			}
11		}
12
13		impl From<$ident> for $crate::Cursor {
14			fn from(value: $ident) -> Self {
15				value.0.into()
16			}
17		}
18
19		impl From<$ident> for $crate::Token {
20			fn from(value: $ident) -> Self {
21				value.0.into()
22			}
23		}
24
25		impl $crate::ToSpan for $ident {
26			fn to_span(&self) -> Span {
27				self.0.to_span()
28			}
29		}
30
31		impl $crate::SemanticEq for $ident {
32			fn semantic_eq(&self, s: &Self, source_text: &str) -> bool {
33				self.0.semantic_eq(&s.0, source_text)
34			}
35		}
36	};
37}
38
39/// Shared body for [define_kinds!] and [define_fixed_kinds!]; everything except the
40/// `SemanticEq` impl, which differs between the two (see [define_fixed_kinds!]).
41macro_rules! define_kind_common {
42	($(#[$meta:meta])* $ident:ident) => {
43		$(#[$meta])*
44		#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
46		pub struct $ident($crate::Cursor);
47
48		impl $ident {
49			pub const fn dummy() -> Self {
50				Self($crate::Cursor::dummy($crate::Token::dummy($crate::Kind::$ident)))
51			}
52
53			pub fn associated_whitespace(&self) -> $crate::AssociatedWhitespaceRules {
54				self.0.token().associated_whitespace()
55			}
56
57			pub fn with_associated_whitespace(&self, rules: $crate::AssociatedWhitespaceRules) -> Self {
58				Self(self.0.map_token(|t| t.with_associated_whitespace(rules)))
59			}
60		}
61
62		impl $crate::ToCursors for $ident {
63			fn to_cursors(&self, s: &mut impl $crate::CursorSink) {
64				s.append((*self).into());
65			}
66		}
67
68		impl<'a> $crate::Peek<'a> for $ident {
69			const PEEK_KINDSET: $crate::KindSet = $crate::KindSet::new(&[$crate::Kind::$ident]);
70		}
71
72		impl<'a> $crate::Parse<'a> for $ident {
73			fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
74			where
75				I: ::std::iter::Iterator<Item = $crate::Cursor> + ::std::clone::Clone,
76			{
77				let c = p.next();
78				if Self::peek(p, c) { Ok(Self(c)) } else { Err($crate::Diagnostic::new(c, $crate::Diagnostic::unexpected))? }
79			}
80		}
81
82
83		impl From<$ident> for $crate::Cursor {
84			fn from(value: $ident) -> Self {
85				value.0.into()
86			}
87		}
88
89		impl From<$ident> for $crate::Token {
90			fn from(value: $ident) -> Self {
91				value.0.into()
92			}
93		}
94
95		impl $crate::ToSpan for $ident {
96			fn to_span(&self) -> $crate::Span {
97				self.0.to_span()
98			}
99		}
100	};
101}
102
103macro_rules! define_kinds {
104	($($(#[$meta:meta])* $ident:ident,)*) => {
105		$(
106		define_kind_common!($(#[$meta])* $ident);
107
108		impl $crate::SemanticEq for $ident {
109			fn semantic_eq(&self, s: &Self, source_text: &str) -> bool {
110				self.0.semantic_eq(&s.0, source_text)
111			}
112		}
113		)*
114	};
115}
116
117/// Like [define_kinds!], but for kinds whose content is entirely fixed by the Rust type - once
118/// parsing succeeds there is no varying data left to compare (e.g. a [Comma] is always just a
119/// `,`; the only bits that could otherwise differ are non-semantic associated-whitespace
120/// formatting hints). `semantic_eq` for these kinds is therefore always `true`, skipping the
121/// token comparison outright.
122macro_rules! define_fixed_kinds {
123	($($(#[$meta:meta])* $ident:ident,)*) => {
124		$(
125		define_kind_common!($(#[$meta])* $ident);
126
127		impl $crate::SemanticEq for $ident {
128			#[inline(always)]
129			fn semantic_eq(&self, _: &Self, _source_text: &str) -> bool {
130				true
131			}
132		}
133		)*
134	};
135}
136
137macro_rules! define_kind_idents {
138	($($(#[$meta:meta])* $ident:ident,)*) => {
139		$(
140		$(#[$meta])*
141		#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
142		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
143		pub struct $ident($crate::Cursor);
144
145		impl $crate::ToCursors for $ident {
146			fn to_cursors(&self, s: &mut impl $crate::CursorSink) {
147				s.append((*self).into());
148			}
149		}
150
151		impl<'a> $crate::Peek<'a> for $ident {
152			const PEEK_KINDSET: $crate::KindSet = $crate::KindSet::new(&[$crate::Kind::$ident]);
153		}
154
155		impl<'a> $crate::Parse<'a> for $ident {
156			fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
157			where
158				I: ::std::iter::Iterator<Item = $crate::Cursor> + ::std::clone::Clone,
159			{
160				let c = p.next();
161				if Self::peek(p, c) { Ok(Self(c)) } else { Err($crate::Diagnostic::new(c, $crate::Diagnostic::unexpected))? }
162			}
163		}
164
165
166		impl From<$ident> for $crate::Kind {
167			fn from(value: $ident) -> Self {
168				value.0.into()
169			}
170		}
171
172		impl From<$ident> for $crate::Cursor {
173			fn from(value: $ident) -> Self {
174				value.0
175			}
176		}
177
178		impl From<$ident> for $crate::Token {
179			fn from(value: $ident) -> Self {
180				value.0.into()
181			}
182		}
183
184		impl $crate::ToSpan for $ident {
185			fn to_span(&self) -> $crate::Span {
186				self.0.to_span()
187			}
188		}
189
190		impl $crate::SemanticEq for $ident {
191			fn semantic_eq(&self, s: &Self, source_text: &str) -> bool {
192				self.0.semantic_eq(&s.0, source_text)
193			}
194		}
195
196		impl $ident {
197			/// Checks if the ident begins with two HYPHEN MINUS (`--`) characters.
198			pub fn is_dashed_ident(&self) -> bool {
199				self.0.token().is_dashed_ident()
200			}
201
202			pub const fn dummy() -> Self {
203				Self($crate::Cursor::dummy($crate::Token::dummy($crate::Kind::$ident)))
204			}
205		}
206		)*
207	};
208}
209
210/// A macro for defining a struct which captures a [Kind::Delim][Kind::Delim] with a specific character.
211///
212/// # Example
213///
214/// ```
215/// use css_parse::*;
216/// custom_delim!{
217///   /// A £ character.
218///   PoundSterling, '£'
219/// }
220///
221/// assert_parse!(EmptyAtomSet::ATOMS, PoundSterling, "£");
222/// ```
223#[macro_export]
224macro_rules! custom_delim {
225	($(#[$meta:meta])* $ident:ident, $ch:literal) => {
226		$(#[$meta])*
227		#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
228		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
229		pub struct $ident($crate::T![Delim]);
230
231		impl $ident {
232			pub fn associated_whitespace(&self) -> $crate::AssociatedWhitespaceRules {
233				self.0.associated_whitespace()
234			}
235
236			pub fn with_associated_whitespace(&self, rules: $crate::AssociatedWhitespaceRules) -> Self {
237				Self(self.0.with_associated_whitespace(rules))
238			}
239		}
240
241		impl $crate::ToCursors for $ident {
242			fn to_cursors(&self, s: &mut impl $crate::CursorSink) {
243				s.append((*self).into());
244			}
245		}
246
247		impl<'a> $crate::Peek<'a> for $ident {
248			fn peek<I>(_: &$crate::Parser<'a, I>, c: $crate::Cursor) -> bool
249			where
250				I: ::std::iter::Iterator<Item = $crate::Cursor> + ::std::clone::Clone,
251			{
252				c == $crate::Kind::Delim && c == $ch
253			}
254		}
255
256		impl<'a> $crate::Parse<'a> for $ident {
257			fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
258			where
259				I: ::std::iter::Iterator<Item = $crate::Cursor> + ::std::clone::Clone,
260			{
261				use $crate::Peek;
262				let delim = p.parse::<$crate::T![Delim]>()?;
263				if Self::peek(p, delim.into()) {
264					Ok(Self(delim))
265				} else {
266					Err($crate::Diagnostic::new(delim.into(), $crate::Diagnostic::unexpected))?
267				}
268			}
269		}
270
271
272
273		impl From<$ident> for $crate::Cursor {
274			fn from(value: $ident) -> Self {
275				value.0.into()
276			}
277		}
278
279		impl $crate::ToSpan for $ident {
280			fn to_span(&self) -> $crate::Span {
281				self.0.to_span()
282			}
283		}
284
285		impl PartialEq<char> for $ident {
286			fn eq(&self, other: &char) -> bool {
287				self.0 == *other
288			}
289		}
290
291		impl $crate::SemanticEq for $ident {
292			#[inline(always)]
293			fn semantic_eq(&self, _: &Self, _source_text: &str) -> bool {
294				// The character is fixed by the type itself (parsing only succeeds for
295				// `$ch`), so there is nothing left to compare.
296				true
297			}
298		}
299	};
300}
301
302/// A macro for defining a struct which captures two adjacent [Kind::Delim][Kind::Delim] tokens, each with a
303/// specific character.
304///
305/// # Example
306///
307/// ```
308/// use css_parse::*;
309/// custom_double_delim!{
310///   /// Two % adjacent symbols
311///   DoublePercent, '%', '%'
312/// }
313///
314/// assert_parse!(EmptyAtomSet::ATOMS, DoublePercent, "%%");
315/// ```
316#[macro_export]
317macro_rules! custom_double_delim {
318	($(#[$meta:meta])*$ident: ident, $first: literal, $second: literal) => {
319		$(#[$meta])*
320		#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
321		#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
322		pub struct $ident(pub $crate::T![Delim], pub $crate::T![Delim]);
323
324		impl $ident {
325			pub const fn dummy() -> Self {
326				Self(<$crate::T![Delim]>::dummy(), <$crate::T![Delim]>::dummy())
327			}
328		}
329
330		impl<'a> $crate::Peek<'a> for $ident {
331			fn peek<I>(p: &$crate::Parser<'a, I>, c: $crate::Cursor) -> bool
332			where
333				I: ::std::iter::Iterator<Item = $crate::Cursor> + ::std::clone::Clone,
334			{
335				c == $first && p.peek_n(2) == $second
336			}
337		}
338
339		impl<'a> $crate::Parse<'a> for $ident {
340			fn parse<I>(p: &mut $crate::Parser<'a, I>) -> $crate::Result<Self>
341			where
342				I: ::std::iter::Iterator<Item = $crate::Cursor> + ::std::clone::Clone,
343			{
344				let first = p.parse::<$crate::T![Delim]>()?;
345				if first != $first {
346					let c: Cursor = first.into();
347					Err($crate::Diagnostic::new(c, $crate::Diagnostic::expected_delim))?;
348				}
349				let skip = p.set_skip($crate::KindSet::NONE);
350				let second = p.parse::<$crate::T![Delim]>();
351				p.set_skip(skip);
352				let second = second?;
353				if second != $second {
354					let c:Cursor = second.into();
355					Err($crate::Diagnostic::new(c, $crate::Diagnostic::expected_delim))?;
356				}
357				Ok(Self(first, second))
358			}
359		}
360
361		impl<'a> $crate::ToCursors for $ident {
362			fn to_cursors(&self, s: &mut impl $crate::CursorSink) {
363				s.append(self.0.into());
364				s.append(self.1.into());
365			}
366		}
367
368		impl $crate::ToSpan for $ident {
369			fn to_span(&self) -> $crate::Span {
370				self.0.to_span() + self.1.to_span()
371			}
372		}
373
374		impl $crate::SemanticEq for $ident {
375			#[inline(always)]
376			fn semantic_eq(&self, _: &Self, _source_text: &str) -> bool {
377				// Both characters are fixed by the type itself (`$first` then `$second`), so
378				// there is nothing left to compare.
379				true
380			}
381		}
382	};
383}
384
385define_kinds! {
386	/// Represents a token with [Kind::Eof][Kind::Eof]. Use [T![Eof]][crate::T] to refer to this.
387	Eof,
388
389	/// Represents a token with [Kind::Comment][Kind::Comment]. Use [T![Comment]][crate::T] to refer to this.
390	Comment,
391
392	/// Represents a token with [Kind::CdcOrCdo][Kind::CdcOrCdo]. Use [T![CdcOrCdo]][crate::T] to refer to this.
393	CdcOrCdo,
394
395	/// Represents a token with [Kind::BadString][Kind::BadString]. Use [T![BadString]][crate::T] to refer to this.
396	BadString,
397
398	/// Represents a token with [Kind::BadUrl][Kind::BadUrl]. Use [T![BadUrl]][crate::T] to refer to this.[
399	BadUrl,
400
401	/// Represents a token with [Kind::Delim][Kind::Delim], can be any single character. Use [T![Delim]][crate::T] to refer to this.
402	Delim,
403}
404
405define_fixed_kinds! {
406	/// Represents a token with [Kind::Colon][Kind::Colon] - a `:` character. Use [T![:]][crate::T] to refer to this.
407	Colon,
408
409	/// Represents a token with [Kind::Semicolon][Kind::Semicolon] - a `;` character. Use [T![;]][crate::T] to refer to this.
410	Semicolon,
411
412	/// Represents a token with [Kind::Comma][Kind::Comma] - a `,` character. Use [T![,]][crate::T] to refer to this.
413	Comma,
414
415	/// Represents a token with [Kind::LeftCurly][Kind::LeftCurly] - a `{` character. Use [T!['{']][crate::T] to refer to this.
416	LeftCurly,
417
418	/// Represents a token with [Kind::LeftCurly][Kind::LeftCurly] - a `}` character. Use [T!['}']][crate::T] to refer to this.
419	RightCurly,
420
421	/// Represents a token with [Kind::LeftSquare][Kind::LeftSquare] - a `[` character. Use [T!['[']][crate::T] to refer to this.
422	LeftSquare,
423
424	/// Represents a token with [Kind::RightSquare][Kind::RightSquare] - a `]` character. Use [T![']']][crate::T] to refer to this.
425	RightSquare,
426
427	/// Represents a token with [Kind::LeftParen][Kind::LeftParen] - a `(` character. Use [T!['(']][crate::T] to refer to this.
428	LeftParen,
429
430	/// Represents a token with [Kind::RightParen][Kind::RightParen] - a `(` character. Use [T![')']][crate::T] to refer to this.
431	RightParen,
432}
433
434impl PartialEq<char> for Delim {
435	fn eq(&self, other: &char) -> bool {
436		self.0 == *other
437	}
438}
439
440define_kind_idents! {
441	/// Represents a token with [Kind::Ident][Kind::Ident]. Use [T![Ident]][crate::T] to refer to this.
442	Ident,
443
444	/// Represents a token with [Kind::String][Kind::String]. Use [T![String]][crate::T] to refer to this.
445	String,
446
447	/// Represents a token with [Kind::Url][Kind::Url]. Use [T![Url]][crate::T] to refer to this.
448	Url,
449
450	/// Represents a token with [Kind::Function][Kind::Function]. Use [T![Function]][crate::T] to refer to this.
451	Function,
452
453	/// Represents a token with [Kind::AtKeyword][Kind::AtKeyword]. Use [T![AtKeyword]][crate::T] to refer to this.
454	AtKeyword,
455
456	/// Represents a token with [Kind::Hash][Kind::Hash]. Use [T![Hash]][crate::T] to refer to this.
457	Hash,
458}
459
460/// Represents a token with [Kind::Whitespace]. Use [T![Whitespace]][crate::T] to refer to
461/// this.
462#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
463#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
464pub struct Whitespace(Cursor);
465cursor_wrapped!(Whitespace);
466
467impl Whitespace {
468	/// Returns a new [Whitespace] with the significance flag set to `significant`.
469	///
470	/// Whitespace parsed with [Parse] is significant by default: a [Whitespace] in an AST node carries meaning
471	/// (a descendant combinator, or the space in `@charset "utf-8";`) and minifiers must keep it. Set this to
472	/// `false` for whitespace kept only as trivia, which a minifier is free to remove.
473	pub fn with_significant_whitespace(&self, significant: bool) -> Self {
474		Self(self.0.map_token(|t| t.with_significant_whitespace(significant)))
475	}
476}
477
478impl<'a> Peek<'a> for Whitespace {
479	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Whitespace]);
480
481	fn peek<I>(p: &Parser<'a, I>, _: Cursor) -> bool
482	where
483		I: Iterator<Item = Cursor> + Clone,
484	{
485		// Whitespace needs to peek its own cursor because it was likely given one that skipped Whitespace.
486		let c = p.peek_n_with_skip(1, KindSet::COMMENTS);
487		c == Kind::Whitespace
488	}
489}
490
491impl<'a> Parse<'a> for Whitespace {
492	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
493	where
494		I: Iterator<Item = Cursor> + Clone,
495	{
496		// Whitespace needs to implement parse so that it can change the skip-state to only ensuring Whitespace
497		// is not ignored.
498		let skip = p.set_skip(KindSet::COMMENTS);
499		let c = p.next();
500		p.set_skip(skip);
501		if c != Kind::Whitespace {
502			Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))?
503		}
504		Ok(Self(c.map_token(|t| t.with_significant_whitespace(true))))
505	}
506}
507
508/// Represents a token with [Kind::Ident] that also begins with two HYPHEN MINUS (`--`)
509/// characters. Use [T![DashedIdent]][crate::T] to refer to this.
510#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
511#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
512pub struct DashedIdent(Ident);
513cursor_wrapped!(DashedIdent);
514
515impl<'a> Peek<'a> for DashedIdent {
516	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Ident]);
517
518	#[inline(always)]
519	fn peek<I>(_: &Parser<'a, I>, c: Cursor) -> bool
520	where
521		I: Iterator<Item = Cursor> + Clone,
522	{
523		c == Kind::Ident && c.token().is_dashed_ident()
524	}
525}
526
527impl<'a> Parse<'a> for DashedIdent {
528	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
529	where
530		I: Iterator<Item = Cursor> + Clone,
531	{
532		let c = p.next();
533		if Self::peek(p, c) {
534			Ok(Self(Ident(c)))
535		} else {
536			Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))?
537		}
538	}
539}
540
541/// Represents a token with [Kind::Dimension]. Use [T![Dimension]][crate::T] to refer to this.
542#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
543#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
544pub struct Dimension(Cursor);
545cursor_wrapped!(Dimension);
546
547impl PartialEq<f32> for Dimension {
548	fn eq(&self, other: &f32) -> bool {
549		self.0.token().value() == *other
550	}
551}
552
553impl<'a> Peek<'a> for Dimension {
554	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Dimension]);
555}
556
557impl<'a> Parse<'a> for Dimension {
558	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
559	where
560		I: Iterator<Item = Cursor> + Clone,
561	{
562		let c = p.next();
563		if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
564	}
565}
566
567impl From<Dimension> for f32 {
568	fn from(val: Dimension) -> Self {
569		val.0.token().value()
570	}
571}
572
573impl ToNumberValue for Dimension {
574	fn to_number_value(&self) -> Option<f32> {
575		Some(self.0.token().value())
576	}
577}
578
579impl Dimension {
580	/// Returns the [f32] representation of the dimension's value.
581	pub fn value(&self) -> f32 {
582		self.0.token().value()
583	}
584
585	/// Marks the dimension's type (integer or number) as required, so minification preserves it.
586	pub fn preserve_type(self) -> Self {
587		Self(self.0.map_token(|t| t.with_numeric_type_required()))
588	}
589}
590
591/// Represents a token with [Kind::Number]. Use [T![Number]][crate::T] to refer to this.
592#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
593#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
594pub struct Number(Cursor);
595cursor_wrapped!(Number);
596
597impl Number {
598	pub const NUMBER_ZERO: Number = Number(Cursor::dummy(Token::NUMBER_ZERO));
599	pub const ZERO: Number = Number(Cursor::dummy(Token::NUMBER_ZERO));
600
601	/// Returns the [f32] representation of the number's value.
602	pub fn value(&self) -> f32 {
603		self.0.token().value()
604	}
605
606	pub fn is_int(&self) -> bool {
607		self.0.token().is_int()
608	}
609
610	pub fn is_float(&self) -> bool {
611		self.0.token().is_float()
612	}
613
614	pub fn has_sign(&self) -> bool {
615		self.0.token().has_sign()
616	}
617
618	pub fn preserve_sign(self) -> Self {
619		if self.has_sign() { Self(self.0.map_token(|t| t.with_sign_required())) } else { self }
620	}
621
622	/// Marks the number's type (integer or number) as required, so minification preserves it.
623	pub fn preserve_type(self) -> Self {
624		Self(self.0.map_token(|t| t.with_numeric_type_required()))
625	}
626}
627
628impl<'a> Peek<'a> for Number {
629	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Number]);
630}
631
632impl<'a> Parse<'a> for Number {
633	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
634	where
635		I: Iterator<Item = Cursor> + Clone,
636	{
637		let c = p.next();
638		if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
639	}
640}
641
642impl From<Number> for f32 {
643	fn from(value: Number) -> Self {
644		value.value()
645	}
646}
647
648impl From<Number> for i32 {
649	fn from(value: Number) -> Self {
650		value.value() as i32
651	}
652}
653
654impl PartialEq<f32> for Number {
655	fn eq(&self, other: &f32) -> bool {
656		self.value() == *other
657	}
658}
659
660impl ToNumberValue for Number {
661	fn to_number_value(&self) -> Option<f32> {
662		Some(self.value())
663	}
664}
665
666impl ToNormalisedValue for Number {
667	fn to_normalised_value(&self) -> Option<f32> {
668		self.to_number_value()
669	}
670}
671
672/// Various [T!s][crate::T] representing a tokens with [Kind::Delim], but each represents a discrete character.
673pub mod delim {
674	custom_delim! {
675		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `&`. Use [T![&]][crate::T] to
676		/// refer to this.
677		And, '&'
678	}
679	custom_delim! {
680		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `@`. Use [T![@]][crate::T] to
681		/// refer to this. Not to be conused with [T![AtKeyword]][crate::T] which represents a token with
682		/// [Kind::AtKeyword][crate::Kind::AtKeyword].
683		At, '@'
684	}
685	custom_delim! {
686		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `^`. Use [T![^]][crate::T] to
687		/// refer to this.
688		Caret, '^'
689	}
690	custom_delim! {
691		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `-`. Use [T![-]][crate::T] to
692		/// refer to this.
693		Dash, '-'
694	}
695	custom_delim! {
696		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `$`. Use [T![$]][crate::T] to
697		/// refer to this.
698		Dollar, '$'
699	}
700	custom_delim! {
701		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `.`. Use [T![.]][crate::T] to
702		/// refer to this.
703		Dot, '.'
704	}
705	custom_delim! {
706		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `=`. Use [T![=]][crate::T] to
707		/// refer to this.
708		Eq, '='
709	}
710	custom_delim! {
711		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `>`. Use [T![>]][crate::T] to
712		/// refer to this.
713		Gt, '>'
714	}
715	custom_delim! {
716		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `#`. Use [T![#]][crate::T] to
717		/// refer to this. Not to be conused with [T![Hash]][crate::T] which represents a token with
718		/// [Kind::Hash][crate::Kind::Hash].
719		Hash, '#'
720	}
721	custom_delim! {
722		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `<`. Use [T![<]][crate::T] to
723		/// refer to this.
724		Lt, '<'
725	}
726	custom_delim! {
727		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `!`. Use [T![!]][crate::T] to
728		/// refer to this.
729		Bang, '!'
730	}
731	custom_delim! {
732		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `|`. Use [T![|]][crate::T] to
733		/// refer to this.
734		Or, '|'
735	}
736	custom_delim! {
737		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `%`. Use [T![%]][crate::T] to
738		/// refer to this.
739		Percent, '%'
740	}
741	custom_delim! {
742		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `+`. Use [T![+]][crate::T] to
743		/// refer to this.
744		Plus, '+'
745	}
746	custom_delim! {
747		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `?`. Use [T![?]][crate::T] to
748		/// refer to this.
749		Question, '?'
750	}
751	custom_delim! {
752		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `/`. Use [T![/]][crate::T] to
753		/// refer to this.
754		Slash, '/'
755	}
756	custom_delim! {
757		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `*`. Use [T![*]][crate::T] to
758		/// refer to this.
759		Star, '*'
760	}
761	custom_delim! {
762		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `~`. Use [T![~]][crate::T] to
763		/// refer to this.
764		Tilde, '~'
765	}
766	custom_delim! {
767		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char `_`. Use [T![_]][crate::T] to
768		/// refer to this.
769		Underscore, '_'
770	}
771	custom_delim! {
772		/// Represents a token with [Kind::Delim][crate::Kind::Delim] that has the char ```. Use [T!['`']][crate::T] to
773		/// refer to this.
774		Backtick, '`'
775	}
776}
777
778/// Various [T!s][crate::T] representing two consecutive tokens that cannot be separated by any other tokens. These are
779/// convenient as it can be tricky to parse two consecutive tokens given the default behaviour of the parser is to skip
780/// whitespace and comments.
781pub mod double {
782	use crate::{
783		Cursor, CursorSink, Kind, KindSet, Parse, Parser, Peek, Result, SemanticEq, Span, T, ToCursors, ToSpan,
784	};
785
786	custom_double_delim! {
787		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
788		/// other token. The first token has the char `>` while the second has the char `=`, representing `>=`. Use
789		/// [T![>=]][crate::T] to refer to this.
790		GreaterThanEqual, '>', '='
791	}
792	custom_double_delim! {
793		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
794		/// other token. The first token has the char `<` while the second has the char `=`, representing `<=`. Use
795		/// [T![<=]][crate::T] to refer to this.
796		LessThanEqual, '<', '='
797	}
798	custom_double_delim! {
799		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
800		/// other token. The first token has the char `*` while the second has the char `|`, representing `*|`. Use
801		/// [T![*|]][crate::T] to refer to this.
802		StarPipe, '*', '|'
803	}
804	custom_double_delim! {
805		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
806		/// other token. The first token has the char `|` while the second has the char `|`, representing `||`. Use
807		/// [T![||]][crate::T] to refer to this.
808		PipePipe, '|', '|'
809	}
810	custom_double_delim! {
811		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
812		/// other token. The first token has the char `=` while the second has the char `=`, representing `==`. Use
813		/// [T![==]][crate::T] to refer to this.
814		EqualEqual, '=', '='
815	}
816	custom_double_delim! {
817		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
818		/// other token. The first token has the char `~` while the second has the char `=`, representing `~=`. Use
819		/// [T![~=]][crate::T] to refer to this.
820		TildeEqual, '~', '='
821	}
822	custom_double_delim! {
823		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
824		/// other token. The first token has the char `|` while the second has the char `=`, representing `|=`. Use
825		/// [T![|=]][crate::T] to refer to this.
826		PipeEqual, '|', '='
827	}
828	custom_double_delim! {
829		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
830		/// other token. The first token has the char `^` while the second has the char `=`, representing `^=`. Use
831		/// [T![\^=]][crate::T] to refer to this.
832		CaretEqual, '^', '='
833	}
834	custom_double_delim! {
835		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
836		/// other token. The first token has the char `$` while the second has the char `=`, representing `$=`. Use
837		/// [T![$=]][crate::T] to refer to this.
838		DollarEqual, '$', '='
839	}
840	custom_double_delim! {
841		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
842		/// other token. The first token has the char `*` while the second has the char `=`, representing `*=`. Use
843		/// [T![*=]][crate::T] to refer to this.
844		StarEqual, '*', '='
845	}
846	custom_double_delim! {
847		/// Represents a two consecutive tokens with [Kind::Delim][crate::Kind::Delim] that cannot be separated by any
848		/// other token. The first token has the char `!` while the second has the char `=`, representing `!=`. Use
849		/// [T![!=]][crate::T] to refer to this.
850		BangEqual, '*', '='
851	}
852
853	/// Represents a two consecutive tokens with [Kind::Colon] that cannot be separated by any other token, representing
854	/// `::`. Use [T![::]][crate::T] to refer to this.
855	#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
856	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
857	pub struct ColonColon(T![:], T![:]);
858
859	impl ColonColon {
860		pub const fn dummy() -> Self {
861			Self(<T![:]>::dummy(), <T![:]>::dummy())
862		}
863	}
864
865	impl<'a> Peek<'a> for ColonColon {
866		fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
867		where
868			I: Iterator<Item = Cursor> + Clone,
869		{
870			c == Kind::Colon && p.peek_n(2) == Kind::Colon
871		}
872	}
873
874	impl<'a> Parse<'a> for ColonColon {
875		fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
876		where
877			I: Iterator<Item = Cursor> + Clone,
878		{
879			let first = p.parse::<T![:]>()?;
880			let skip = p.set_skip(KindSet::NONE);
881			let second = p.parse::<T![:]>();
882			p.set_skip(skip);
883			Ok(Self(first, second?))
884		}
885	}
886
887	impl ToCursors for ColonColon {
888		fn to_cursors(&self, s: &mut impl CursorSink) {
889			s.append(self.0.into());
890			s.append(self.1.into());
891		}
892	}
893
894	impl ToSpan for ColonColon {
895		fn to_span(&self) -> Span {
896			self.0.to_span() + self.1.to_span()
897		}
898	}
899
900	impl SemanticEq for ColonColon {
901		#[inline(always)]
902		fn semantic_eq(&self, _: &Self, _source_text: &str) -> bool {
903			// Both `:` characters are fixed by the type itself, so there is nothing left to
904			// compare.
905			true
906		}
907	}
908}
909
910/// Represents any possible single token. Use [T![Any]][crate::T] to refer to this.
911#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
912#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
913pub struct Any(Cursor);
914cursor_wrapped!(Any);
915
916impl<'a> Peek<'a> for Any {
917	fn peek<I>(_: &Parser<'a, I>, _: Cursor) -> bool
918	where
919		I: Iterator<Item = Cursor> + Clone,
920	{
921		true
922	}
923}
924
925impl<'a> Parse<'a> for Any {
926	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
927	where
928		I: Iterator<Item = Cursor> + Clone,
929	{
930		let c = p.next();
931		Ok(Self(c))
932	}
933}
934
935/// Represents a token with either [Kind::LeftCurly], [Kind::LeftParen] or [Kind::LeftSquare]. Use
936/// [T![PairWiseStart]][crate::T] to refer to this.
937#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
938#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
939pub struct PairWiseStart(Cursor);
940cursor_wrapped!(PairWiseStart);
941
942impl PairWiseStart {
943	pub fn kind(&self) -> Kind {
944		self.0.token().kind()
945	}
946
947	pub fn end(&self) -> Kind {
948		match self.kind() {
949			Kind::LeftCurly => Kind::RightCurly,
950			Kind::LeftParen => Kind::RightParen,
951			Kind::LeftSquare => Kind::RightSquare,
952			k => k,
953		}
954	}
955}
956
957impl<'a> Peek<'a> for PairWiseStart {
958	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly, Kind::LeftSquare, Kind::LeftParen]);
959}
960
961impl<'a> Parse<'a> for PairWiseStart {
962	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
963	where
964		I: Iterator<Item = Cursor> + Clone,
965	{
966		let c = p.next();
967		if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
968	}
969}
970
971/// Represents a token with either [Kind::RightCurly], [Kind::RightParen] or [Kind::RightSquare]. Use
972/// [T![PairWiseEnd]][crate::T] to refer to this.
973#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
974#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
975pub struct PairWiseEnd(Cursor);
976cursor_wrapped!(PairWiseEnd);
977
978impl PairWiseEnd {
979	pub fn kind(&self) -> Kind {
980		self.0.token().kind()
981	}
982
983	pub fn start(&self) -> Kind {
984		match self.kind() {
985			Kind::RightCurly => Kind::LeftCurly,
986			Kind::RightParen => Kind::LeftParen,
987			Kind::RightSquare => Kind::LeftSquare,
988			k => k,
989		}
990	}
991}
992
993impl<'a> Peek<'a> for PairWiseEnd {
994	const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::RightCurly, Kind::RightSquare, Kind::RightParen]);
995}
996
997impl<'a> Parse<'a> for PairWiseEnd {
998	fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
999	where
1000		I: Iterator<Item = Cursor> + Clone,
1001	{
1002		let c = p.next();
1003		if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
1004	}
1005}
1006
1007/// The [T!][crate::T] macro expands to the name of a type representing the Token of the same name. These can be used in struct
1008/// fields to type child nodes.
1009#[macro_export]
1010macro_rules! T {
1011	[:] => { $crate::token_macros::Colon };
1012	[;] => { $crate::token_macros::Semicolon };
1013	[,] => { $crate::token_macros::Comma };
1014	['{'] => { $crate::token_macros::LeftCurly };
1015	['}'] => { $crate::token_macros::RightCurly };
1016	['['] => { $crate::token_macros::LeftSquare };
1017	[']'] => { $crate::token_macros::RightSquare };
1018	['('] => { $crate::token_macros::LeftParen };
1019	[')'] => { $crate::token_macros::RightParen };
1020	[' '] => { $crate::token_macros::Whitespace };
1021
1022	[&] => { $crate::token_macros::delim::And };
1023	[@] => { $crate::token_macros::delim::At };
1024	[^] => { $crate::token_macros::delim::Caret };
1025	[-] => { $crate::token_macros::delim::Dash };
1026	[$] => { $crate::token_macros::delim::Dollar };
1027	[.] => { $crate::token_macros::delim::Dot };
1028	[=] => { $crate::token_macros::delim::Eq };
1029	[>] => { $crate::token_macros::delim::Gt };
1030	[#] => { $crate::token_macros::delim::Hash };
1031	[<] => { $crate::token_macros::delim::Lt };
1032	[!] => { $crate::token_macros::delim::Bang };
1033	[|] => { $crate::token_macros::delim::Or };
1034	[%] => { $crate::token_macros::delim::Percent };
1035	[+] => { $crate::token_macros::delim::Plus };
1036	[?] => { $crate::token_macros::delim::Question };
1037	[/] => { $crate::token_macros::delim::Slash };
1038	[*] => { $crate::token_macros::delim::Star };
1039	[~] => { $crate::token_macros::delim::Tilde };
1040	[_] => { $crate::token_macros::delim::Underscore };
1041	['`'] => { $crate::token_macros::delim::Backtick };
1042
1043	[>=] => { $crate::token_macros::double::GreaterThanEqual };
1044	[<=] => { $crate::token_macros::double::LessThanEqual };
1045	[*|] => { $crate::token_macros::double::StarPipe };
1046	[::] => { $crate::token_macros::double::ColonColon };
1047	[||] => { $crate::token_macros::double::PipePipe };
1048	[==] => { $crate::token_macros::double::EqualEqual };
1049	[~=] => { $crate::token_macros::double::TildeEqual };
1050	[|=] => { $crate::token_macros::double::PipeEqual };
1051	[^=] => { $crate::token_macros::double::CaretEqual };
1052	["$="] => { $crate::token_macros::double::DollarEqual };
1053	[*=] => { $crate::token_macros::double::StarEqual };
1054	[!=] => { $crate::token_macros::double::BangEqual };
1055
1056	[Dimension::$ident: ident] => { $crate::token_macros::dimension::$ident };
1057
1058	[!important] => { $crate::token_macros::double::BangImportant };
1059
1060	[$ident:ident] => { $crate::token_macros::$ident }
1061}
1062
1063#[cfg(test)]
1064mod fixed_kind_semantic_eq_tests {
1065	use super::*;
1066	use crate::SemanticEq;
1067	use css_lexer::{AssociatedWhitespaceRules, SourceOffset};
1068
1069	// Colon, Semicolon, Comma, and the paren/curly/square brackets are "delim-like": they
1070	// share Delim's bit layout and can carry non-semantic associated-whitespace formatting
1071	// hints, which makes two otherwise-identical tokens compare unequal via plain `PartialEq`.
1072	// `semantic_eq` must ignore this entirely for these kinds, since there is no other varying
1073	// content once the type is known.
1074	#[test]
1075	fn fixed_punctuation_kinds_are_always_semantic_eq() {
1076		macro_rules! check {
1077			($ty:ident, $token:expr) => {{
1078				let plain = $ty(Cursor::new(SourceOffset(0), $token));
1079				let with_rule = $ty(Cursor::new(
1080					SourceOffset(0),
1081					$token.with_associated_whitespace(AssociatedWhitespaceRules::EnforceBefore),
1082				));
1083				assert_ne!(
1084					plain,
1085					with_rule,
1086					"associated whitespace should still affect PartialEq for {}",
1087					stringify!($ty)
1088				);
1089				assert!(
1090					plain.semantic_eq(&with_rule, ""),
1091					"{} should always be semantic_eq regardless of associated whitespace",
1092					stringify!($ty)
1093				);
1094			}};
1095		}
1096		check!(Colon, Token::COLON);
1097		check!(Semicolon, Token::SEMICOLON);
1098		check!(Comma, Token::COMMA);
1099		check!(LeftCurly, Token::LEFT_CURLY);
1100		check!(RightCurly, Token::RIGHT_CURLY);
1101		check!(LeftSquare, Token::LEFT_SQUARE);
1102		check!(RightSquare, Token::RIGHT_SQUARE);
1103		check!(LeftParen, Token::LEFT_PAREN);
1104		check!(RightParen, Token::RIGHT_PAREN);
1105	}
1106}