Skip to content

Commit d5964ed

Browse files
committed
refactor parser for color function parameters
1 parent 4e1aa68 commit d5964ed

5 files changed

Lines changed: 308 additions & 130 deletions

File tree

src/parser.rs

Lines changed: 50 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::utils::parse_value;
22
use crate::utils::remap;
3+
use crate::utils::ParamParser;
34
use crate::{Color, ParseColorError};
45

56
#[cfg(feature = "named-colors")]
@@ -46,23 +47,26 @@ pub fn parse(s: &str) -> Result<Color, ParseColorError> {
4647
Err(e) => e,
4748
};
4849

49-
if !s.is_ascii() {
50-
return Err(err);
51-
}
52-
5350
if let (Some(idx), Some(s)) = (s.find('('), s.strip_suffix(')')) {
54-
let mut params = split_by_space(&s[idx + 1..]);
51+
if !s.is_ascii() {
52+
return Err(err);
53+
}
54+
55+
let mut pp = ParamParser::new(&s[idx + 1..]);
56+
pp.space();
5557

56-
if let Some(s) = params.next() {
58+
if let Some(s) = pp.value() {
5759
if !s.eq_ignore_ascii_case("from") {
5860
return Err(err);
5961
}
6062
} else {
6163
return Err(err);
6264
};
6365

66+
pp.space();
67+
6468
// parse next value as color
65-
let color = if let Some(s) = params.next() {
69+
let color = if let Some(s) = pp.value() {
6670
if let Ok(color) = parse(s) {
6771
color
6872
} else {
@@ -72,21 +76,26 @@ pub fn parse(s: &str) -> Result<Color, ParseColorError> {
7276
return Err(err);
7377
};
7478

75-
let (Some(val1), Some(val2), Some(val3)) = (params.next(), params.next(), params.next())
79+
pp.space();
80+
81+
let (Some(val1), true, Some(val2), true, Some(val3)) =
82+
(pp.value(), pp.space(), pp.value(), pp.space(), pp.value())
7683
else {
7784
return Err(err);
7885
};
7986

80-
let val4 = match params.next() {
81-
Some("/") => {
82-
if let (Some(alpha), None) = (params.next(), params.next()) {
83-
alpha
84-
} else {
85-
return Err(err);
86-
}
87+
pp.space();
88+
89+
let val4 = if pp.is_end() {
90+
"alpha"
91+
} else if let (true, Some(alpha)) = (pp.slash(), pp.value()) {
92+
pp.space();
93+
if !pp.is_end() {
94+
return Err(err);
8795
}
88-
Some(_) => return Err(err),
89-
None => "alpha",
96+
alpha
97+
} else {
98+
return Err(err);
9099
};
91100

92101
match err {
@@ -252,9 +261,6 @@ fn parse_abs(s: &str) -> Result<Color, ParseColorError> {
252261

253262
if let (Some(idx), Some(s)) = (s.find('('), s.strip_suffix(')')) {
254263
let fname = &s[..idx].trim_end();
255-
let mut params = s[idx + 1..]
256-
.split(&[',', '/'])
257-
.flat_map(str::split_ascii_whitespace);
258264

259265
let err = match fname {
260266
s if s.eq_ignore_ascii_case("rgb") || s.eq_ignore_ascii_case("rgba") => {
@@ -276,13 +282,32 @@ fn parse_abs(s: &str) -> Result<Color, ParseColorError> {
276282
_ => return Err(ParseColorError::InvalidFunction),
277283
};
278284

279-
let (Some(val0), Some(val1), Some(val2)) = (params.next(), params.next(), params.next())
280-
else {
285+
let s = &s[idx + 1..];
286+
287+
if !s.is_ascii() {
288+
return Err(err);
289+
}
290+
291+
let mut pp = ParamParser::new(s);
292+
pp.space();
293+
294+
let (Some(val0), true, Some(val1), true, Some(val2)) = (
295+
pp.value(),
296+
pp.comma_or_space(),
297+
pp.value(),
298+
pp.comma_or_space(),
299+
pp.value(),
300+
) else {
281301
return Err(err);
282302
};
283303

284-
let alpha = if let Some(a) = params.next() {
285-
if params.next().is_some() {
304+
let is_space = pp.space();
305+
306+
let alpha = if pp.is_end() {
307+
1.0
308+
} else if let (true, Some(a)) = (pp.comma_or_slash() || is_space, pp.value()) {
309+
pp.space();
310+
if !pp.is_end() {
286311
return Err(err);
287312
}
288313
if let Some((v, _)) = parse_percent_or_float(a) {
@@ -291,7 +316,7 @@ fn parse_abs(s: &str) -> Result<Color, ParseColorError> {
291316
return Err(err);
292317
}
293318
} else {
294-
1.0
319+
return Err(err);
295320
};
296321

297322
match err {
@@ -508,62 +533,6 @@ fn parse_hex(s: &str) -> Result<Color, ParseColorError> {
508533
}
509534
}
510535

511-
struct SplitBySpace<'a> {
512-
s: &'a str,
513-
pos: usize,
514-
inside: usize,
515-
}
516-
517-
impl<'a> Iterator for SplitBySpace<'a> {
518-
type Item = &'a str;
519-
520-
fn next(&mut self) -> Option<Self::Item> {
521-
if self.pos >= self.s.len() {
522-
return None;
523-
}
524-
525-
let start = self.pos;
526-
let mut found_value = false;
527-
528-
for (i, c) in self.s[self.pos..].chars().enumerate() {
529-
if c.is_whitespace() && self.inside == 0 {
530-
if found_value {
531-
let end = self.pos + i;
532-
self.pos = end + 1;
533-
return Some(&self.s[start..end]);
534-
}
535-
self.pos += 1;
536-
return self.next();
537-
} else if c == '(' {
538-
self.inside += 1;
539-
found_value = true;
540-
} else if c == ')' {
541-
if self.inside > 0 {
542-
self.inside -= 1;
543-
}
544-
found_value = true;
545-
} else if !c.is_whitespace() {
546-
found_value = true;
547-
}
548-
}
549-
550-
if found_value {
551-
self.pos = self.s.len();
552-
Some(&self.s[start..])
553-
} else {
554-
None
555-
}
556-
}
557-
}
558-
559-
fn split_by_space(s: &str) -> SplitBySpace<'_> {
560-
SplitBySpace {
561-
s,
562-
pos: 0,
563-
inside: 0,
564-
}
565-
}
566-
567536
// strip suffix ignore case
568537
fn strip_suffix<'a>(s: &'a str, suffix: &str) -> Option<&'a str> {
569538
if suffix.len() > s.len() {
@@ -700,53 +669,4 @@ mod t {
700669
cmp!("abcdef", "ABCDEF");
701670
cmp!("Ff03E0cB", "fF03e0Cb");
702671
}
703-
704-
#[test]
705-
fn split_by_space_() {
706-
let mut iter = split_by_space("");
707-
assert_eq!(iter.next(), None);
708-
709-
let mut iter = split_by_space(" ");
710-
assert_eq!(iter.next(), None);
711-
712-
let mut iter = split_by_space(" ( ) ");
713-
assert_eq!(iter.next(), Some("( )"));
714-
assert_eq!(iter.next(), None);
715-
716-
let mut iter = split_by_space(" x ");
717-
assert_eq!(iter.next(), Some("x"));
718-
assert_eq!(iter.next(), None);
719-
720-
let s = "pink";
721-
let mut iter = split_by_space(s);
722-
assert_eq!(iter.next(), Some("pink"));
723-
assert_eq!(iter.next(), None);
724-
725-
let s = ") (ab cd) (";
726-
let mut iter = split_by_space(s);
727-
assert_eq!(iter.next(), Some(")"));
728-
assert_eq!(iter.next(), Some("(ab cd)"));
729-
assert_eq!(iter.next(), Some("("));
730-
assert_eq!(iter.next(), None);
731-
732-
let s = " plum teal f(1, 2, 3) abc ";
733-
let mut iter = split_by_space(s);
734-
assert_eq!(iter.next(), Some("plum"));
735-
assert_eq!(iter.next(), Some("teal"));
736-
assert_eq!(iter.next(), Some("f(1, 2, 3)"));
737-
assert_eq!(iter.next(), Some("abc"));
738-
assert_eq!(iter.next(), None);
739-
740-
let s = "from rgb(from red r g calc(b + 15) / 0.75) h w b / calc(alpha + 0.25) )";
741-
let mut iter = split_by_space(s);
742-
assert_eq!(iter.next(), Some("from"));
743-
assert_eq!(iter.next(), Some("rgb(from red r g calc(b + 15) / 0.75)"));
744-
assert_eq!(iter.next(), Some("h"));
745-
assert_eq!(iter.next(), Some("w"));
746-
assert_eq!(iter.next(), Some("b"));
747-
assert_eq!(iter.next(), Some("/"));
748-
assert_eq!(iter.next(), Some("calc(alpha + 0.25)"));
749-
assert_eq!(iter.next(), Some(")"));
750-
assert_eq!(iter.next(), None);
751-
}
752672
}

src/utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
mod calc;
22
mod helper;
3+
mod param;
34

45
pub use calc::*;
56
pub use helper::*;
7+
pub use param::*;
68

79
use core::f32::consts::{PI, TAU};
810

0 commit comments

Comments
 (0)