1#![deny(warnings)]
2use heck::ToPascalCase;
3use proc_macro2::{Span, TokenStream};
4use quote::{ToTokens, TokenStreamExt, format_ident};
5use std::{
6 fmt::Display,
7 ops::{Deref, Range},
8};
9use syn::{
10 Error, Ident, Lit, LitFloat, LitInt, LitStr, Result, Token, braced, bracketed,
11 ext::IdentExt,
12 parenthesized,
13 parse::{Parse, ParseStream},
14 parse2, token,
15};
16
17#[cfg(test)]
18mod test;
19
20pub struct StrWrapped<T: Parse>(pub T);
21impl<T: Parse> Parse for StrWrapped<T> {
22 fn parse(input_raw: ParseStream) -> Result<Self> {
23 Ok(Self(parse2::<T>(
24 input_raw.parse::<LitStr>()?.value().replace("'", "\"").replace("∞", "").parse::<TokenStream>()?,
25 )?))
26 }
27}
28
29#[derive(Debug, PartialEq, Clone)]
30pub enum Def {
31 Ident(DefIdent),
32 Function(DefIdent, Box<Def>),
33 AutoOr(Box<Def>),
34 NoneOr(Box<Def>),
35 AutoNoneOr(Box<Def>),
36 NormalOr(Box<Def>),
37 Type(DefType),
38 StyleValue(DefType),
39 FunctionType(DefType),
40 Optional(Box<Def>), Combinator(Vec<Def>, DefCombinatorStyle),
42 Group(Box<Def>, DefGroupStyle),
43 Multiplier(Box<Def>, DefMultiplierSeparator, DefRange),
44 Punct(char),
45 IntLiteral(i32),
46 DimensionLiteral(f32, String),
47}
48
49#[derive(Debug, PartialEq, Copy, Clone)]
50pub enum DefGroupStyle {
51 None, OneMustOccur, }
54
55#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
56pub enum DefCombinatorStyle {
57 Ordered, AllMustOccur, Options, Alternatives, }
62
63#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
64pub enum DefMultiplierSeparator {
65 None, Commas, }
68
69#[derive(Debug, PartialEq, Clone)]
70pub enum DefRange {
71 None,
72 Range(Range<f32>), RangeFrom(f32), RangeTo(f32), Fixed(f32), }
77
78#[derive(Debug, PartialEq, Clone)]
79pub struct DefIdent(pub String);
80
81#[derive(Debug, PartialEq, Clone)]
82pub struct DefType {
83 pub ident: DefIdent,
84 pub range: DefRange,
85}
86
87impl DefType {
88 pub fn new(str: &str, range: DefRange) -> Self {
89 DefType { ident: DefIdent(str.to_string()), range }
90 }
91
92 pub fn ident_str(&self) -> &str {
93 self.ident.0.as_str()
94 }
95
96 pub fn maybe_unsized(&self) -> bool {
97 matches!(
99 self.ident_str(),
100 "Color" | "Shadow" | "Image" | "Image1d" | "ContentList" | "CounterStyle" | "CursorImage" | "EasingFunction" | "LineWidthOrRepeat" | "LineWidthList" | "AutoLineWidthList" | "GapRuleList" | "GapAutoRuleList" | "FamilyName" | "BgImage" | "DynamicRangeLimit" | "DynamicRangeLimitMixFunction" | "Outline"
121 | "SingleTransition"
122 | "Symbol" | "TransformList"
124 )
125 }
126}
127
128impl Parse for Def {
129 fn parse(input: ParseStream) -> Result<Self> {
130 let mut root = if input.peek(Token![<]) {
131 input.parse::<Token![<]>()?;
132 let mut style_value = false;
133 let mut function = false;
134 let ident = if input.peek(LitStr) {
135 style_value = true;
136 input.parse::<StrWrapped<DefIdent>>()?.0.0
137 } else {
138 input.parse::<DefIdent>()?.0
139 }
140 .to_pascal_case();
141 let range = if input.peek(token::Bracket) {
142 let content;
143 bracketed!(content in input);
144 content.parse::<DefRange>()?
145 } else {
146 DefRange::None
147 };
148 if input.peek(token::Paren) {
149 let content;
150 parenthesized!(content in input);
151 if !content.is_empty() {
152 Err(Error::new(input.span(), "disallowed content inside deftype function"))?
153 }
154 debug_assert!(!style_value, "Can't be function and style value");
155 function = true;
156 }
157 debug_assert!(!(function && style_value), "Can't be function or style value and or-none");
158 let ty = if let Some(without_auto) = ident.strip_suffix("-or-auto") {
159 Self::AutoOr(Box::new(Def::Type(DefType { ident: DefIdent(without_auto.into()), range })))
160 } else if let Some(without_none) = ident.strip_suffix("-or-none") {
161 Self::NoneOr(Box::new(Def::Type(DefType { ident: DefIdent(without_none.into()), range })))
162 } else if function {
163 Self::FunctionType(DefType { ident: DefIdent(ident), range })
164 } else if style_value {
165 Self::StyleValue(DefType { ident: DefIdent(ident), range })
166 } else {
167 Self::Type(DefType { ident: DefIdent(ident), range })
168 };
169 input.parse::<Token![>]>()?;
170 ty
171 } else if input.peek(token::Bracket) {
172 let content;
173 bracketed!(content in input);
174 let inner = Box::new(content.parse::<Def>()?);
175 if input.peek(Token![!]) {
176 input.parse::<Token![!]>()?;
177 Self::Group(inner, DefGroupStyle::OneMustOccur)
178 } else if input.peek(Token![#]) {
179 input.parse::<Token![#]>()?;
180 Self::Multiplier(inner, DefMultiplierSeparator::Commas, DefRange::RangeFrom(1.))
181 } else if input.peek(Token![+]) {
182 input.parse::<Token![+]>()?;
183 Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::RangeFrom(1.))
184 } else if input.peek(token::Brace) {
185 let content;
186 braced!(content in input);
187 let range = content.parse::<DefRange>()?;
188 debug_assert!(matches!(range, DefRange::Range(_) | DefRange::Fixed(_)));
189 Self::Multiplier(inner, DefMultiplierSeparator::None, range)
190 } else {
191 Self::Group(inner, DefGroupStyle::None)
192 }
193 } else if input.peek(Ident::peek_any) {
194 let ident = input.parse::<DefIdent>()?;
195 if input.peek(token::Paren) {
196 let content;
197 parenthesized!(content in input);
198 Self::Function(ident, Box::new(content.parse::<Def>()?))
199 } else {
200 Self::Ident(ident)
201 }
202 } else if input.peek(Lit) {
203 let lit = input.parse::<Lit>()?;
204 match lit {
205 Lit::Int(lit) => {
206 if lit.suffix() == "" {
207 Self::IntLiteral(lit.base10_parse::<i32>()?)
208 } else {
209 let unit = lit.suffix();
210 if unit.is_empty() {
211 Err(Error::new(lit.span(), "Invalid dimension unit"))?
212 }
213 Self::DimensionLiteral(lit.base10_parse::<f32>()?, unit.to_string())
214 }
215 }
216 Lit::Char(lit) => Self::Punct(lit.value()),
217 Lit::Str(lit) if lit.value().len() == 1 => Self::Punct(lit.value().chars().next().unwrap()),
218 _ => Err(Error::new(input.span(), "unknown token in Def parse"))?,
219 }
220 } else {
221 input.step(|cursor| {
222 if let Some((p, next)) = cursor.punct() {
223 return Ok((Self::Punct(p.as_char()), next));
224 }
225 Err(Error::new(input.span(), "unknown token in Def parse"))?
226 })?
227 }
228 .optimize();
229 loop {
230 if input.is_empty() {
231 return Ok(root);
232 } else if input.peek(Token![?]) {
233 input.parse::<Token![?]>()?;
234 let inner = root;
235 root = Self::Optional(Box::new(inner.optimize()));
236 } else if input.peek(Token![+])
237 || input.peek(Token![#])
238 || input.peek(token::Brace)
239 || input.peek(Token![*])
240 {
241 let inner = root;
242 let (sep, range) = if input.peek(Token![*]) {
243 input.parse::<Token![*]>()?;
244 (DefMultiplierSeparator::None, DefRange::RangeFrom(0.))
245 } else if input.peek(Token![+]) {
246 input.parse::<Token![+]>()?;
247 (DefMultiplierSeparator::None, DefRange::RangeFrom(1.))
248 } else if input.peek(Token![#]) {
249 input.parse::<Token![#]>()?;
250 let range = if input.peek(token::Brace) {
251 let content;
252 braced!(content in input);
253 content.parse::<DefRange>()?
254 } else if input.peek(Token![?]) {
255 input.parse::<Token![?]>()?;
256 DefRange::RangeFrom(0.)
257 } else {
258 DefRange::RangeFrom(1.)
259 };
260 (DefMultiplierSeparator::Commas, range)
261 } else if input.peek(token::Brace) {
262 let content;
263 braced!(content in input);
264 (DefMultiplierSeparator::None, content.parse::<DefRange>()?)
265 } else {
266 Err(Error::new(input.span(), "Unknown token in DefMultiplierStyle parse!"))?
267 };
268 root = Self::Multiplier(Box::new(inner.optimize()), sep, range).optimize();
269 } else {
270 let style = if input.peek(Token![||]) {
271 input.parse::<Token![||]>()?;
272 DefCombinatorStyle::Options
273 } else if input.peek(Token![|]) {
274 input.parse::<Token![|]>()?;
275 DefCombinatorStyle::Alternatives
276 } else if input.peek(Token![&&]) {
277 input.parse::<Token![&&]>()?;
278 DefCombinatorStyle::AllMustOccur
279 } else {
280 DefCombinatorStyle::Ordered
281 };
282 let mut next = input.parse::<Def>()?;
283 match (&mut root, &mut next) {
284 (_, &mut Self::Combinator(ref mut children, ref s)) if s == &style => {
285 children.insert(0, root);
286 root = next;
287 }
288 (&mut Self::Combinator(ref mut children, ref s), _) if s == &style => {
289 children.push(next);
290 }
291 (_, &mut Self::Combinator(ref mut children, ref other_style)) if &style < other_style => {
292 let options = Self::Combinator(vec![root, children.remove(0)], style);
293 children.insert(0, options);
294 root = next;
295 }
296 (_, Self::Group(inner, DefGroupStyle::None)) => {
297 let children = vec![root, inner.as_ref().clone()];
298 root = Self::Combinator(children, style);
299 }
300 (Self::Group(inner, DefGroupStyle::None), _) => {
301 let children = vec![inner.as_ref().clone(), next];
302 root = Self::Combinator(children, style);
303 }
304 _ => {
305 let children = vec![root, next];
306 root = Self::Combinator(children, style);
307 }
308 }
309 }
310 }
311 }
312}
313
314impl Def {
315 pub fn maybe_unsized(&self) -> bool {
318 match self {
319 Self::Ident(_) | Self::IntLiteral(_) | Self::DimensionLiteral(_, _) | Self::Punct(_) => false,
320 Self::Function(_, inner) => inner.maybe_unsized(),
322 Self::FunctionType(ty) => {
323 matches!(ty.ident_str(), "DynamicRangeLimitMix" | "Param" | "Repeat")
324 }
325 Self::Type(d) => d.maybe_unsized(),
326 Self::StyleValue(ty) => {
327 matches!(
328 ty.ident_str(),
329 "BorderBlockStart"
330 | "BorderTopColor" | "CaretColor"
331 | "ColumnRuleWidth"
332 | "DynamicRangeLimit"
333 | "EventTriggerName"
334 | "EventTriggerSource"
335 | "OutlineColor" | "PointerTimelineAxis"
336 | "PointerTimelineName"
337 | "AnimationRangeStart"
338 | "AnimationRangeEnd"
339 | "ScrollTimelineAxis"
340 | "ScrollTimelineName"
341 | "ViewTimelineAxis"
342 | "ViewTimelineName"
343 | "BorderTopClip" | "ColumnRule"
344 | "RowRule" | "TimelineTriggerActivationRange"
345 | "TimelineTriggerActivationRangeEnd"
346 | "TimelineTriggerActivationRangeStart"
347 | "TimelineTriggerActiveRange"
348 | "TimelineTriggerActiveRangeEnd"
349 | "TimelineTriggerActiveRangeStart"
350 | "TimelineTriggerName"
351 | "TimelineTriggerSource"
352 )
353 }
354 Self::AutoOr(d) | Self::NoneOr(d) | Self::AutoNoneOr(d) | Self::NormalOr(d) => d.maybe_unsized(),
355 Self::Optional(d) => d.maybe_unsized(),
356 Self::Combinator(ds, _) => ds.iter().any(|d| d.maybe_unsized()),
357 Self::Group(d, _) => d.maybe_unsized(),
358 Self::Multiplier(_, _, _) => true,
359 }
360 }
361
362 pub fn suggested_data_type(&self) -> DataType {
363 match self {
364 Self::Combinator(_, DefCombinatorStyle::Alternatives) => DataType::Enum,
365 _ => DataType::SingleUnnamedStruct,
366 }
367 }
368
369 pub fn optimize(&self) -> Self {
370 match self {
371 Self::Combinator(defs, DefCombinatorStyle::Alternatives) if defs.len() == 2 => {
372 let [first, second] = defs.as_slice() else { panic!("defs.len() was 2!") };
373 match (first, second) {
374 (Def::Ident(DefIdent(ident)), Def::AutoOr(def))
376 | (Def::AutoOr(def), Def::Ident(DefIdent(ident)))
377 if ident == "none" =>
378 {
379 Def::AutoNoneOr(Box::new(*def.clone()))
380 }
381 (Def::Ident(DefIdent(ident)), Def::NoneOr(def))
383 | (Def::NoneOr(def), Def::Ident(DefIdent(ident)))
384 if ident == "auto" =>
385 {
386 Def::AutoNoneOr(Box::new(*def.clone()))
387 }
388 (Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
390 if ident == "auto" &&
391 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
393 {
394 Def::AutoOr(Box::new(def.clone()))
395 }
396 (Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
398 if ident == "none" &&
399 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
401 {
402 Def::NoneOr(Box::new(def.clone()))
403 }
404 (Def::Ident(DefIdent(ident)), def) | (def, Def::Ident(DefIdent(ident)))
406 if ident == "normal" &&
407 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_) | Def::NormalOr(_)) =>
409 {
410 Def::NormalOr(Box::new(def.clone()))
411 }
412 (Def::Type(type1), Def::Type(type2)) => match (type1.ident_str(), type2.ident_str()) {
414 ("GapRuleList", "GapAutoRuleList") => {
416 Def::Type(DefType::new("GapRuleList", type1.range.clone()))
417 }
418 ("GapAutoRuleList", "GapRuleList") => {
419 Def::Type(DefType::new("GapRuleList", type2.range.clone()))
420 }
421 ("LengthPercentage", "Flex") | ("Flex", "LengthPercentage") => {
422 Def::Type(DefType::new("LengthPercentageOrFlex", type1.range.clone()))
423 }
424 ("Number", "Percentage") | ("Percentage", "Number") => {
425 Def::Type(DefType::new("NumberPercentage", type1.range.clone()))
426 }
427 ("Number", "Length") | ("Length", "Number") => {
428 Def::Type(DefType::new("NumberLength", type1.range.clone()))
429 }
430 _ => {
431 return Self::Combinator(
432 vec![first.optimize(), second.optimize()],
433 DefCombinatorStyle::Alternatives,
434 );
435 }
436 },
437 _ => {
438 return Self::Combinator(
439 vec![first.optimize(), second.optimize()],
440 DefCombinatorStyle::Alternatives,
441 );
442 }
443 }
444 }
445 Self::Combinator(defs, DefCombinatorStyle::Alternatives) if defs.len() == 3 => {
446 let [first, second, third] = defs.as_slice() else { panic!("defs.len() was 3!") };
447 match (first, second, third) {
448 (def, Def::Ident(DefIdent(first)), Def::Ident(DefIdent(second)))
450 | (Def::Ident(DefIdent(first)), def, Def::Ident(DefIdent(second)))
451 | (Def::Ident(DefIdent(first)), Def::Ident(DefIdent(second)), def)
452 if matches!((first.as_str(), second.as_str()), ("auto", "none") | ("none", "auto")) &&
453 !matches!(def, Def::Ident(_) | Def::AutoOr(_) | Def::NoneOr(_)) =>
455 {
456 Def::AutoNoneOr(Box::new(def.clone()))
457 }
458 (Def::Type(type1), Def::Type(type2), Def::Ident(DefIdent(ident)))
460 | (Def::Ident(DefIdent(ident)), Def::Type(type1), Def::Type(type2))
461 | (Def::Type(type1), Def::Ident(DefIdent(ident)), Def::Type(type2))
462 if ident == "auto" =>
463 {
464 match (type1.ident_str(), type2.ident_str()) {
465 ("Number", "Length") | ("Length", "Number") => {
466 Def::AutoOr(Box::new(Def::Type(DefType::new("NumberLength", type1.range.clone()))))
467 }
468 ("Percentage", "Length") | ("Length", "Percentage") => {
469 Def::AutoOr(Box::new(Def::Type(DefType::new("LengthPercentage", type1.range.clone()))))
470 }
471 _ => {
472 return Self::Combinator(
473 vec![first.optimize(), second.optimize(), third.optimize()],
474 DefCombinatorStyle::Alternatives,
475 );
476 }
477 }
478 }
479 (def, Def::Type(type1), Def::Type(type2))
482 | (Def::Type(type1), def, Def::Type(type2))
483 | (Def::Type(type1), Def::Type(type2), def) => match (type1.ident_str(), type2.ident_str()) {
484 ("LengthPercentage", "Flex") | ("Flex", "LengthPercentage") => Def::Combinator(
485 vec![
486 def.optimize(),
487 Def::Type(DefType::new("LengthPercentageOrFlex", type1.range.clone())),
488 ],
489 DefCombinatorStyle::Alternatives,
490 ),
491 ("Number", "Percentage") | ("Percentage", "Number") => Def::Combinator(
492 vec![def.optimize(), Def::Type(DefType::new("NumberPercentage", type1.range.clone()))],
493 DefCombinatorStyle::Alternatives,
494 ),
495 ("Number", "Length") | ("Length", "Number") => Def::Combinator(
496 vec![def.optimize(), Def::Type(DefType::new("NumberLength", type1.range.clone()))],
497 DefCombinatorStyle::Alternatives,
498 ),
499 _ => {
500 return Self::Combinator(
501 vec![first.optimize(), second.optimize(), third.optimize()],
502 DefCombinatorStyle::Alternatives,
503 );
504 }
505 },
506 _ => {
507 return Self::Combinator(
508 vec![first.optimize(), second.optimize(), third.optimize()],
509 DefCombinatorStyle::Alternatives,
510 );
511 }
512 }
513 }
514 Self::Combinator(defs, style) => {
515 return Self::Combinator(defs.iter().map(|d| d.optimize()).collect(), *style);
516 }
517 Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::Fixed(i)) => {
520 let opts: Vec<_> = (1..=*i as u32).map(|_| inner.deref().clone()).collect();
521 Self::Combinator(opts, DefCombinatorStyle::Ordered)
522 }
523 Self::Multiplier(inner, DefMultiplierSeparator::None, DefRange::Range(Range { start, end })) => {
526 let opts: Vec<Def> = (1..=*end as i32)
527 .map(|i| if i <= (*start as i32) { inner.deref().clone() } else { Self::Optional(inner.clone()) })
528 .collect();
529 Self::Combinator(opts, DefCombinatorStyle::Ordered)
530 }
531 Self::Multiplier(inner, sep, range) => {
532 return Self::Multiplier(Box::new(inner.optimize()), *sep, range.clone());
533 }
534 Self::Optional(inner) => return Self::Optional(Box::new(inner.optimize())),
535 Self::Group(inner, style) => return Self::Group(Box::new(inner.optimize()), *style),
536 _ => return self.clone(),
537 }
538 .optimize()
539 }
540}
541
542impl Parse for DefIdent {
543 fn parse(input: ParseStream) -> Result<Self> {
544 let mut str = "".to_owned();
545 let mut last_was_ident = false;
546 loop {
547 if input.peek(Token![>]) || input.peek(token::Bracket) {
548 return Ok(Self(str));
549 } else if input.peek(Ident::peek_any) && !last_was_ident {
550 last_was_ident = true;
551 let ident = input.call(Ident::parse_any)?;
552 str.push_str(&ident.to_string());
553 } else if input.peek(LitInt) && last_was_ident {
555 last_was_ident = true;
556 let int = input.parse::<LitInt>()?;
557 str.push_str(&int.to_string());
558 } else if input.peek(Token![-]) {
559 last_was_ident = false;
560 input.parse::<Token![-]>()?;
561 str.push('-');
562 } else {
563 return Ok(Self(str));
564 }
565 }
566 }
567}
568
569impl Parse for DefRange {
570 fn parse(input: ParseStream) -> Result<Self> {
571 let mut lhs = None;
572 let mut rhs = None;
573 if input.peek(LitFloat) {
574 lhs = Some(input.parse::<LitFloat>()?.base10_parse()?);
575 } else if input.peek(LitInt) {
576 lhs = Some(input.parse::<LitInt>()?.base10_parse::<f32>()?);
577 }
578 if input.peek(Token![,]) {
579 input.parse::<Token![,]>()?;
580 if input.peek(LitFloat) {
581 rhs = Some(input.parse::<LitFloat>()?.base10_parse()?);
582 } else if input.peek(LitInt) {
583 rhs = Some(input.parse::<LitInt>()?.base10_parse::<f32>()?);
584 }
585 } else if let Some(lhs) = lhs {
586 return Ok(Self::Fixed(lhs));
587 }
588 Ok(match (lhs, rhs) {
589 (Some(start), Some(end)) => Self::Range(Range { start, end }),
590 (None, Some(end)) => Self::RangeTo(end),
591 (Some(start), None) => Self::RangeFrom(start),
592 (None, None) => Self::None,
593 })
594 }
595}
596
597pub enum DataType {
598 SingleUnnamedStruct,
599 Enum,
600}
601
602impl DataType {
603 pub fn is_struct(&self) -> bool {
604 matches!(self, Self::SingleUnnamedStruct)
605 }
606
607 pub fn is_enum(&self) -> bool {
608 matches!(self, Self::Enum)
609 }
610}
611
612impl Display for DefIdent {
613 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
614 self.0.fmt(f)
615 }
616}
617
618impl ToTokens for DefIdent {
619 fn to_tokens(&self, tokens: &mut TokenStream) {
620 tokens.append(Ident::new(&self.to_string(), Span::call_site()));
621 }
622}
623
624impl From<DefIdent> for Ident {
625 fn from(value: DefIdent) -> Self {
626 format_ident!("{}", value.0)
627 }
628}
629
630impl From<Ident> for DefIdent {
631 fn from(value: Ident) -> Self {
632 Self(value.to_string())
633 }
634}