1use super::prelude::*;
2
3#[node]
20#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
22#[derive(csskit_derives::NodeWithMetadata)]
23pub enum Syntax<'a> {
24 Universal(T![*]),
25 Components(SyntaxComponent, Vec<'a, (T![|], SyntaxComponent)>),
26 String(T![String]),
27}
28
29#[node]
37#[derive(ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
39#[derive(csskit_derives::NodeWithMetadata)]
40pub enum SyntaxComponent {
41 Type(T![<], SyntaxTypeName, T![>], Option<SyntaxMultiplier>),
42 TransformList(T![<], T![Ident], T![>]),
43 Keyword(T![Ident], Option<SyntaxMultiplier>),
44}
45
46impl SyntaxComponent {
47 fn parse_bracketed<'a, I>(p: &mut Parser<'a, I>, open: T![<]) -> ParserResult<Self>
48 where
49 I: Iterator<Item = Cursor> + Clone,
50 {
51 let c = p.peek_n(1);
52 if c == Kind::Ident && p.to_atom::<CssAtomSet>(c) == CssAtomSet::TransformList {
53 let ident = p.parse::<T![Ident]>()?;
54 let close = p.parse::<T![>]>()?;
55 return Ok(Self::TransformList(open, ident, close));
56 }
57 let name = p.parse::<SyntaxTypeName>()?;
58 let close = p.parse::<T![>]>()?;
59 let multiplier = p.parse_if_peek::<SyntaxMultiplier>()?;
60 Ok(Self::Type(open, name, close, multiplier))
61 }
62}
63
64impl<'a> Peek<'a> for SyntaxComponent {
65 const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Ident, Kind::Delim]);
66
67 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
68 where
69 I: Iterator<Item = Cursor> + Clone,
70 {
71 c == Kind::Ident || <T![<] as Peek>::peek(p, c)
72 }
73}
74
75impl<'a> Parse<'a> for SyntaxComponent {
76 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
77 where
78 I: Iterator<Item = Cursor> + Clone,
79 {
80 if p.peek::<T![Ident]>() {
81 let ident = p.parse::<T![Ident]>()?;
82 let skip = p.set_skip(KindSet::NONE);
83 let multiplier = p.parse_if_peek::<SyntaxMultiplier>();
84 p.set_skip(skip);
85 return Ok(Self::Keyword(ident, multiplier?));
86 }
87 let open = p.parse::<T![<]>()?;
88 let skip = p.set_skip(KindSet::NONE);
89 let component = Self::parse_bracketed(p, open);
90 p.set_skip(skip);
91 component
92 }
93}
94
95#[node]
102#[derive(
103 Parse, Peek, IntoCursor, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
104)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
106#[derive(csskit_derives::NodeWithMetadata)]
107pub enum SyntaxTypeName {
108 #[atom(CssAtomSet::Angle)]
109 Angle(T![Ident]),
110 #[atom(CssAtomSet::Color)]
111 Color(T![Ident]),
112 #[atom(CssAtomSet::CustomIdent)]
113 CustomIdent(T![Ident]),
114 #[atom(CssAtomSet::Image)]
115 Image(T![Ident]),
116 #[atom(CssAtomSet::Integer)]
117 Integer(T![Ident]),
118 #[atom(CssAtomSet::Length)]
119 Length(T![Ident]),
120 #[atom(CssAtomSet::LengthPercentage)]
121 LengthPercentage(T![Ident]),
122 #[atom(CssAtomSet::Number)]
123 Number(T![Ident]),
124 #[atom(CssAtomSet::Percentage)]
125 Percentage(T![Ident]),
126 #[atom(CssAtomSet::Resolution)]
127 Resolution(T![Ident]),
128 #[atom(CssAtomSet::String)]
129 String(T![Ident]),
130 #[atom(CssAtomSet::Time)]
131 Time(T![Ident]),
132 #[atom(CssAtomSet::Url)]
133 Url(T![Ident]),
134 #[atom(CssAtomSet::TransformFunction)]
135 TransformFunction(T![Ident]),
136}
137
138#[node]
142#[derive(
143 Parse, Peek, IntoCursor, ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
144)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
146#[derive(csskit_derives::NodeWithMetadata)]
147pub enum SyntaxMultiplier {
148 Hash(T![#]),
149 Plus(T![+]),
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use css_parse::{assert_parse, assert_parse_error, assert_peek_false};
156
157 #[test]
158 fn test_writes() {
159 assert_parse!(CssAtomSet::ATOMS, Syntax, "*");
160 assert_parse!(CssAtomSet::ATOMS, Syntax, "auto");
161 assert_parse!(CssAtomSet::ATOMS, Syntax, "auto+");
162 assert_parse!(CssAtomSet::ATOMS, Syntax, "auto#");
163 assert_parse!(CssAtomSet::ATOMS, Syntax, "<length>");
164 assert_parse!(CssAtomSet::ATOMS, Syntax, "<length-percentage>");
165 assert_parse!(CssAtomSet::ATOMS, Syntax, "<custom-ident>");
166 assert_parse!(CssAtomSet::ATOMS, Syntax, "<color>#");
167 assert_parse!(CssAtomSet::ATOMS, Syntax, "<transform-function>+");
168 assert_parse!(CssAtomSet::ATOMS, Syntax, "<transform-list>");
169 assert_parse!(CssAtomSet::ATOMS, Syntax, "<percentage> | <number> | auto");
170 assert_parse!(CssAtomSet::ATOMS, Syntax, "red|<color>");
171 assert_parse!(CssAtomSet::ATOMS, Syntax, "\"<length> | auto\"");
172 }
173
174 #[test]
175 fn test_errors() {
176 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "< length>");
177 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "<length >");
178 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "<length> +");
179 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "<foo>");
180 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "<transform-list>+");
181 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "<length> <color>");
182 assert_parse_error!(CssAtomSet::ATOMS, Syntax, "auto |");
183 assert_peek_false!(CssAtomSet::ATOMS, Syntax, "1");
184 }
185}