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) -> bool {
33 self.0.semantic_eq(&s.0)
34 }
35 }
36 };
37}
38
39macro_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) -> bool {
110 self.0.semantic_eq(&s.0)
111 }
112 }
113 )*
114 };
115}
116
117macro_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) -> 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) -> bool {
192 self.0.semantic_eq(&s.0)
193 }
194 }
195
196 impl $ident {
197 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#[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) -> bool {
294 true
297 }
298 }
299 };
300}
301
302#[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($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) -> bool {
377 true
380 }
381 }
382 };
383}
384
385define_kinds! {
386 Eof,
388
389 Comment,
391
392 CdcOrCdo,
394
395 BadString,
397
398 BadUrl,
400
401 Delim,
403}
404
405define_fixed_kinds! {
406 Colon,
408
409 Semicolon,
411
412 Comma,
414
415 LeftCurly,
417
418 RightCurly,
420
421 LeftSquare,
423
424 RightSquare,
426
427 LeftParen,
429
430 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 Ident,
443
444 String,
446
447 Url,
449
450 Function,
452
453 AtKeyword,
455
456 Hash,
458}
459
460#[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 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 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 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#[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#[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 pub fn value(&self) -> f32 {
582 self.0.token().value()
583 }
584}
585
586#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
588#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
589pub struct Number(Cursor);
590cursor_wrapped!(Number);
591
592impl Number {
593 pub const NUMBER_ZERO: Number = Number(Cursor::dummy(Token::NUMBER_ZERO));
594 pub const ZERO: Number = Number(Cursor::dummy(Token::NUMBER_ZERO));
595
596 pub fn value(&self) -> f32 {
598 self.0.token().value()
599 }
600
601 pub fn is_int(&self) -> bool {
602 self.0.token().is_int()
603 }
604
605 pub fn is_float(&self) -> bool {
606 self.0.token().is_float()
607 }
608
609 pub fn has_sign(&self) -> bool {
610 self.0.token().has_sign()
611 }
612
613 pub fn preserve_sign(self) -> Self {
614 if self.has_sign() { Self(self.0.map_token(|t| t.with_sign_required())) } else { self }
615 }
616}
617
618impl<'a> Peek<'a> for Number {
619 const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Number]);
620}
621
622impl<'a> Parse<'a> for Number {
623 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
624 where
625 I: Iterator<Item = Cursor> + Clone,
626 {
627 let c = p.next();
628 if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
629 }
630}
631
632impl From<Number> for f32 {
633 fn from(value: Number) -> Self {
634 value.value()
635 }
636}
637
638impl From<Number> for i32 {
639 fn from(value: Number) -> Self {
640 value.value() as i32
641 }
642}
643
644impl PartialEq<f32> for Number {
645 fn eq(&self, other: &f32) -> bool {
646 self.value() == *other
647 }
648}
649
650impl ToNumberValue for Number {
651 fn to_number_value(&self) -> Option<f32> {
652 Some(self.value())
653 }
654}
655
656impl ToNormalisedValue for Number {
657 fn to_normalised_value(&self) -> Option<f32> {
658 self.to_number_value()
659 }
660}
661
662pub mod delim {
664 custom_delim! {
665 And, '&'
668 }
669 custom_delim! {
670 At, '@'
674 }
675 custom_delim! {
676 Caret, '^'
679 }
680 custom_delim! {
681 Dash, '-'
684 }
685 custom_delim! {
686 Dollar, '$'
689 }
690 custom_delim! {
691 Dot, '.'
694 }
695 custom_delim! {
696 Eq, '='
699 }
700 custom_delim! {
701 Gt, '>'
704 }
705 custom_delim! {
706 Hash, '#'
710 }
711 custom_delim! {
712 Lt, '<'
715 }
716 custom_delim! {
717 Bang, '!'
720 }
721 custom_delim! {
722 Or, '|'
725 }
726 custom_delim! {
727 Percent, '%'
730 }
731 custom_delim! {
732 Plus, '+'
735 }
736 custom_delim! {
737 Question, '?'
740 }
741 custom_delim! {
742 Slash, '/'
745 }
746 custom_delim! {
747 Star, '*'
750 }
751 custom_delim! {
752 Tilde, '~'
755 }
756 custom_delim! {
757 Underscore, '_'
760 }
761 custom_delim! {
762 Backtick, '`'
765 }
766}
767
768pub mod double {
772 use crate::{
773 Cursor, CursorSink, Kind, KindSet, Parse, Parser, Peek, Result, SemanticEq, Span, T, ToCursors, ToSpan,
774 };
775
776 custom_double_delim! {
777 GreaterThanEqual, '>', '='
781 }
782 custom_double_delim! {
783 LessThanEqual, '<', '='
787 }
788 custom_double_delim! {
789 StarPipe, '*', '|'
793 }
794 custom_double_delim! {
795 PipePipe, '|', '|'
799 }
800 custom_double_delim! {
801 EqualEqual, '=', '='
805 }
806 custom_double_delim! {
807 TildeEqual, '~', '='
811 }
812 custom_double_delim! {
813 PipeEqual, '|', '='
817 }
818 custom_double_delim! {
819 CaretEqual, '^', '='
823 }
824 custom_double_delim! {
825 DollarEqual, '$', '='
829 }
830 custom_double_delim! {
831 StarEqual, '*', '='
835 }
836 custom_double_delim! {
837 BangEqual, '*', '='
841 }
842
843 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
846 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
847 pub struct ColonColon(T![:], T![:]);
848
849 impl ColonColon {
850 pub const fn dummy() -> Self {
851 Self(<T![:]>::dummy(), <T![:]>::dummy())
852 }
853 }
854
855 impl<'a> Peek<'a> for ColonColon {
856 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
857 where
858 I: Iterator<Item = Cursor> + Clone,
859 {
860 c == Kind::Colon && p.peek_n(2) == Kind::Colon
861 }
862 }
863
864 impl<'a> Parse<'a> for ColonColon {
865 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
866 where
867 I: Iterator<Item = Cursor> + Clone,
868 {
869 let first = p.parse::<T![:]>()?;
870 let skip = p.set_skip(KindSet::NONE);
871 let second = p.parse::<T![:]>();
872 p.set_skip(skip);
873 Ok(Self(first, second?))
874 }
875 }
876
877 impl ToCursors for ColonColon {
878 fn to_cursors(&self, s: &mut impl CursorSink) {
879 s.append(self.0.into());
880 s.append(self.1.into());
881 }
882 }
883
884 impl ToSpan for ColonColon {
885 fn to_span(&self) -> Span {
886 self.0.to_span() + self.1.to_span()
887 }
888 }
889
890 impl SemanticEq for ColonColon {
891 #[inline(always)]
892 fn semantic_eq(&self, _: &Self) -> bool {
893 true
896 }
897 }
898}
899
900#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
902#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
903pub struct Any(Cursor);
904cursor_wrapped!(Any);
905
906impl<'a> Peek<'a> for Any {
907 fn peek<I>(_: &Parser<'a, I>, _: Cursor) -> bool
908 where
909 I: Iterator<Item = Cursor> + Clone,
910 {
911 true
912 }
913}
914
915impl<'a> Parse<'a> for Any {
916 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
917 where
918 I: Iterator<Item = Cursor> + Clone,
919 {
920 let c = p.next();
921 Ok(Self(c))
922 }
923}
924
925#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
928#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
929pub struct PairWiseStart(Cursor);
930cursor_wrapped!(PairWiseStart);
931
932impl PairWiseStart {
933 pub fn kind(&self) -> Kind {
934 self.0.token().kind()
935 }
936
937 pub fn end(&self) -> Kind {
938 match self.kind() {
939 Kind::LeftCurly => Kind::RightCurly,
940 Kind::LeftParen => Kind::RightParen,
941 Kind::LeftSquare => Kind::RightSquare,
942 k => k,
943 }
944 }
945}
946
947impl<'a> Peek<'a> for PairWiseStart {
948 const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::LeftCurly, Kind::LeftSquare, Kind::LeftParen]);
949}
950
951impl<'a> Parse<'a> for PairWiseStart {
952 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
953 where
954 I: Iterator<Item = Cursor> + Clone,
955 {
956 let c = p.next();
957 if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
958 }
959}
960
961#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
964#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
965pub struct PairWiseEnd(Cursor);
966cursor_wrapped!(PairWiseEnd);
967
968impl PairWiseEnd {
969 pub fn kind(&self) -> Kind {
970 self.0.token().kind()
971 }
972
973 pub fn start(&self) -> Kind {
974 match self.kind() {
975 Kind::RightCurly => Kind::LeftCurly,
976 Kind::RightParen => Kind::LeftParen,
977 Kind::RightSquare => Kind::LeftSquare,
978 k => k,
979 }
980 }
981}
982
983impl<'a> Peek<'a> for PairWiseEnd {
984 const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::RightCurly, Kind::RightSquare, Kind::RightParen]);
985}
986
987impl<'a> Parse<'a> for PairWiseEnd {
988 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
989 where
990 I: Iterator<Item = Cursor> + Clone,
991 {
992 let c = p.next();
993 if Self::peek(p, c) { Ok(Self(c)) } else { Err(crate::Diagnostic::new(c, crate::Diagnostic::unexpected))? }
994 }
995}
996
997#[macro_export]
1000macro_rules! T {
1001 [:] => { $crate::token_macros::Colon };
1002 [;] => { $crate::token_macros::Semicolon };
1003 [,] => { $crate::token_macros::Comma };
1004 ['{'] => { $crate::token_macros::LeftCurly };
1005 ['}'] => { $crate::token_macros::RightCurly };
1006 ['['] => { $crate::token_macros::LeftSquare };
1007 [']'] => { $crate::token_macros::RightSquare };
1008 ['('] => { $crate::token_macros::LeftParen };
1009 [')'] => { $crate::token_macros::RightParen };
1010 [' '] => { $crate::token_macros::Whitespace };
1011
1012 [&] => { $crate::token_macros::delim::And };
1013 [@] => { $crate::token_macros::delim::At };
1014 [^] => { $crate::token_macros::delim::Caret };
1015 [-] => { $crate::token_macros::delim::Dash };
1016 [$] => { $crate::token_macros::delim::Dollar };
1017 [.] => { $crate::token_macros::delim::Dot };
1018 [=] => { $crate::token_macros::delim::Eq };
1019 [>] => { $crate::token_macros::delim::Gt };
1020 [#] => { $crate::token_macros::delim::Hash };
1021 [<] => { $crate::token_macros::delim::Lt };
1022 [!] => { $crate::token_macros::delim::Bang };
1023 [|] => { $crate::token_macros::delim::Or };
1024 [%] => { $crate::token_macros::delim::Percent };
1025 [+] => { $crate::token_macros::delim::Plus };
1026 [?] => { $crate::token_macros::delim::Question };
1027 [/] => { $crate::token_macros::delim::Slash };
1028 [*] => { $crate::token_macros::delim::Star };
1029 [~] => { $crate::token_macros::delim::Tilde };
1030 [_] => { $crate::token_macros::delim::Underscore };
1031 ['`'] => { $crate::token_macros::delim::Backtick };
1032
1033 [>=] => { $crate::token_macros::double::GreaterThanEqual };
1034 [<=] => { $crate::token_macros::double::LessThanEqual };
1035 [*|] => { $crate::token_macros::double::StarPipe };
1036 [::] => { $crate::token_macros::double::ColonColon };
1037 [||] => { $crate::token_macros::double::PipePipe };
1038 [==] => { $crate::token_macros::double::EqualEqual };
1039 [~=] => { $crate::token_macros::double::TildeEqual };
1040 [|=] => { $crate::token_macros::double::PipeEqual };
1041 [^=] => { $crate::token_macros::double::CaretEqual };
1042 ["$="] => { $crate::token_macros::double::DollarEqual };
1043 [*=] => { $crate::token_macros::double::StarEqual };
1044 [!=] => { $crate::token_macros::double::BangEqual };
1045
1046 [Dimension::$ident: ident] => { $crate::token_macros::dimension::$ident };
1047
1048 [!important] => { $crate::token_macros::double::BangImportant };
1049
1050 [$ident:ident] => { $crate::token_macros::$ident }
1051}
1052
1053#[cfg(test)]
1054mod fixed_kind_semantic_eq_tests {
1055 use super::*;
1056 use crate::SemanticEq;
1057 use css_lexer::{AssociatedWhitespaceRules, SourceOffset};
1058
1059 #[test]
1065 fn fixed_punctuation_kinds_are_always_semantic_eq() {
1066 macro_rules! check {
1067 ($ty:ident, $token:expr) => {{
1068 let plain = $ty(Cursor::new(SourceOffset(0), $token));
1069 let with_rule = $ty(Cursor::new(
1070 SourceOffset(0),
1071 $token.with_associated_whitespace(AssociatedWhitespaceRules::EnforceBefore),
1072 ));
1073 assert_ne!(
1074 plain,
1075 with_rule,
1076 "associated whitespace should still affect PartialEq for {}",
1077 stringify!($ty)
1078 );
1079 assert!(
1080 plain.semantic_eq(&with_rule),
1081 "{} should always be semantic_eq regardless of associated whitespace",
1082 stringify!($ty)
1083 );
1084 }};
1085 }
1086 check!(Colon, Token::COLON);
1087 check!(Semicolon, Token::SEMICOLON);
1088 check!(Comma, Token::COMMA);
1089 check!(LeftCurly, Token::LEFT_CURLY);
1090 check!(RightCurly, Token::RIGHT_CURLY);
1091 check!(LeftSquare, Token::LEFT_SQUARE);
1092 check!(RightSquare, Token::RIGHT_SQUARE);
1093 check!(LeftParen, Token::LEFT_PAREN);
1094 check!(RightParen, Token::RIGHT_PAREN);
1095 }
1096}