Skip to main content

css_parse/
token_macros.rs

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